From eb0e0c26d464c6b0e13a0c96591eedecfde61aa8 Mon Sep 17 00:00:00 2001 From: huoyo <1729913829@qq.com> Date: Wed, 9 Oct 2024 23:56:30 +0800 Subject: [PATCH] cherry-pick from spring2:v2.5.0 --- .../kotime/controller/KoClassController.java | 87 +++ .../kotime/controller/KoConfigController.java | 60 ++ .../controller/KoCpuUsageController.java | 31 + .../KoDynamicPropertyController.java | 65 ++ .../controller/KoExceptionController.java | 44 ++ .../controller/KoGcUsageController.java | 31 + .../kotime/controller/KoInitController.java | 28 +- .../controller/KoInterfaceController.java | 118 ++++ .../controller/KoJvmSpaceController.java | 57 ++ .../kotime/controller/KoMemoryController.java | 87 +++ .../kotime/controller/KoThreadController.java | 62 ++ .../kotime/controller/KoTimeController.java | 376 ------------ .../java/cn/langpy/kotime/data/DataBase.java | 11 + .../cn/langpy/kotime/data/MemoryBase.java | 9 + .../java/cn/langpy/kotime/data/RedisBase.java | 10 + .../cn/langpy/kotime/model/ClassUsage.java | 31 + .../model/{CpuInfo.java => CpuUsage.java} | 2 +- .../kotime/model/GarbageCollectionInfo.java | 40 ++ .../java/cn/langpy/kotime/model/GcUsage.java | 49 ++ ...HeapMemoryInfo.java => JvmMemoryInfo.java} | 2 +- .../java/cn/langpy/kotime/model/KoResult.java | 58 ++ .../langpy/kotime/model/SystemStatistic.java | 28 +- .../cn/langpy/kotime/model/ThreadInfo.java | 9 + .../cn/langpy/kotime/model/ThreadUsage.java | 31 + .../langpy/kotime/service/ClassService.java | 31 - .../kotime/service/HeapDumpService.java | 45 -- .../kotime/service/SysUsageService.java | 144 ----- .../kotime/service/core/SystemService.java | 77 +++ .../ClassInfoService.java} | 20 +- .../service/metric/CpuMetricService.java | 57 ++ .../service/metric/GcMetricService.java | 64 ++ .../service/metric/JvmSpaceMetricService.java | 57 ++ .../service/metric/MemoryMetricService.java | 127 ++++ .../ThreadMetricService.java} | 41 +- src/main/resources/koapp.properties | 2 +- src/main/resources/kostatic/common.css | 52 ++ .../kostatic/dict/chinese.properties | 27 +- .../kostatic/dict/english.properties | 29 +- src/main/resources/kostatic/operate-en.js | 343 ----------- src/main/resources/kostatic/util.js | 21 +- src/main/resources/kotime.html | 561 ++++++++++++++---- 41 files changed, 1939 insertions(+), 1085 deletions(-) create mode 100644 src/main/java/cn/langpy/kotime/controller/KoClassController.java create mode 100644 src/main/java/cn/langpy/kotime/controller/KoConfigController.java create mode 100644 src/main/java/cn/langpy/kotime/controller/KoCpuUsageController.java create mode 100644 src/main/java/cn/langpy/kotime/controller/KoDynamicPropertyController.java create mode 100644 src/main/java/cn/langpy/kotime/controller/KoExceptionController.java create mode 100644 src/main/java/cn/langpy/kotime/controller/KoGcUsageController.java create mode 100644 src/main/java/cn/langpy/kotime/controller/KoInterfaceController.java create mode 100644 src/main/java/cn/langpy/kotime/controller/KoJvmSpaceController.java create mode 100644 src/main/java/cn/langpy/kotime/controller/KoMemoryController.java create mode 100644 src/main/java/cn/langpy/kotime/controller/KoThreadController.java delete mode 100644 src/main/java/cn/langpy/kotime/controller/KoTimeController.java create mode 100644 src/main/java/cn/langpy/kotime/model/ClassUsage.java rename src/main/java/cn/langpy/kotime/model/{CpuInfo.java => CpuUsage.java} (97%) create mode 100644 src/main/java/cn/langpy/kotime/model/GarbageCollectionInfo.java create mode 100644 src/main/java/cn/langpy/kotime/model/GcUsage.java rename src/main/java/cn/langpy/kotime/model/{HeapMemoryInfo.java => JvmMemoryInfo.java} (96%) create mode 100644 src/main/java/cn/langpy/kotime/model/KoResult.java create mode 100644 src/main/java/cn/langpy/kotime/model/ThreadUsage.java delete mode 100644 src/main/java/cn/langpy/kotime/service/ClassService.java delete mode 100644 src/main/java/cn/langpy/kotime/service/HeapDumpService.java delete mode 100644 src/main/java/cn/langpy/kotime/service/SysUsageService.java create mode 100644 src/main/java/cn/langpy/kotime/service/core/SystemService.java rename src/main/java/cn/langpy/kotime/service/{JvmAttachClassService.java => metric/ClassInfoService.java} (81%) create mode 100644 src/main/java/cn/langpy/kotime/service/metric/CpuMetricService.java create mode 100644 src/main/java/cn/langpy/kotime/service/metric/GcMetricService.java create mode 100644 src/main/java/cn/langpy/kotime/service/metric/JvmSpaceMetricService.java create mode 100644 src/main/java/cn/langpy/kotime/service/metric/MemoryMetricService.java rename src/main/java/cn/langpy/kotime/service/{ThreadUsageService.java => metric/ThreadMetricService.java} (58%) delete mode 100644 src/main/resources/kostatic/operate-en.js diff --git a/src/main/java/cn/langpy/kotime/controller/KoClassController.java b/src/main/java/cn/langpy/kotime/controller/KoClassController.java new file mode 100644 index 0000000..39ad433 --- /dev/null +++ b/src/main/java/cn/langpy/kotime/controller/KoClassController.java @@ -0,0 +1,87 @@ +package cn.langpy.kotime.controller; + +import cn.langpy.kotime.annotation.Auth; +import cn.langpy.kotime.model.KoResult; +import cn.langpy.kotime.service.core.SystemService; +import cn.langpy.kotime.service.metric.ClassInfoService; +import org.springframework.stereotype.Controller; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.logging.Logger; + + +/** + * zhangchang + */ +@Controller +@RequestMapping("/koTime/classes") +public class KoClassController { + private static Logger log = Logger.getLogger(KoClassController.class.toString()); + + @GetMapping("/usage") + @ResponseBody + @Auth + public KoResult usage() { + ClassInfoService instance = SystemService.getInstance(ClassInfoService.class); + return KoResult.success(instance.getClassUsage()); + } + + @PutMapping("/{className}/replace") + @ResponseBody + @Auth + public KoResult updateClass(@RequestParam("classFile") MultipartFile classFile, @PathVariable("className") String className) { + if (classFile == null || classFile.isEmpty()) { + return KoResult.failed("文件不能为空"); + } + if (!StringUtils.hasText(className)) { + return KoResult.failed("类名不能为空"); + } + className = className.trim(); + File file = null; + try { + String originalFilename = classFile.getOriginalFilename(); + if (!originalFilename.endsWith(".class")) { + return KoResult.failed("仅支持.class文件"); + } + String[] filename = originalFilename.split("\\."); + String substring = className.substring(className.lastIndexOf(".") + 1); + if (!substring.equals(filename[0])) { + return KoResult.failed("请确认类名是否正确"); + } + file = uploadFile(classFile.getBytes(), filename[0]); + } catch (IOException e) { + log.severe("Error class file!"); + return KoResult.failed("无法解析文件"); + } + final ClassInfoService classService = SystemService.getInstance(ClassInfoService.class); + classService.updateClass(className, file.getAbsolutePath()); + file.deleteOnExit(); + return KoResult.success("更新成功"); + } + + + private static File uploadFile(byte[] file, String fileName) throws IOException { + FileOutputStream out = null; + try { + File targetFile = File.createTempFile(fileName, ".class", new File(System.getProperty("java.io.tmpdir"))); + out = new FileOutputStream(targetFile.getAbsolutePath()); + out.write(file); + out.flush(); + return targetFile; + } catch (Exception e) { + log.severe("" + e); + } finally { + if (out != null) { + out.flush(); + out.close(); + } + } + return null; + } + +} diff --git a/src/main/java/cn/langpy/kotime/controller/KoConfigController.java b/src/main/java/cn/langpy/kotime/controller/KoConfigController.java new file mode 100644 index 0000000..814d688 --- /dev/null +++ b/src/main/java/cn/langpy/kotime/controller/KoConfigController.java @@ -0,0 +1,60 @@ +package cn.langpy.kotime.controller; + +import cn.langpy.kotime.annotation.Auth; +import cn.langpy.kotime.config.DefaultConfig; + +import cn.langpy.kotime.model.KoResult; +import cn.langpy.kotime.util.Context; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import java.util.logging.Logger; + + +/** + * zhangchang + */ +@Controller +@RequestMapping("/koTime/configs") +public class KoConfigController { + private static Logger log = Logger.getLogger(KoConfigController.class.toString()); + + @GetMapping + @ResponseBody + @Auth + public KoResult getConfig() { + DefaultConfig config = Context.getConfig(); + return KoResult.success(config); + } + + + @PutMapping + @ResponseBody + @Auth + public KoResult updateConfig(@RequestBody DefaultConfig config) { + DefaultConfig koTimeConfig = Context.getConfig(); + if (config.getEnable() != null) { + koTimeConfig.setEnable(config.getEnable()); + } + if (config.getExceptionEnable() != null) { + koTimeConfig.setExceptionEnable(config.getExceptionEnable()); + } + if (config.getLogEnable() != null) { + koTimeConfig.setLogEnable(config.getLogEnable()); + } + if (config.getMailEnable() != null) { + koTimeConfig.setMailEnable(config.getMailEnable()); + } + if (config.getAbbreviationEnable() != null) { + koTimeConfig.setAbbreviationEnable(config.getAbbreviationEnable()); + } + if (config.getThreshold() != null) { + koTimeConfig.setThreshold(config.getThreshold()); + } + if (config.getLanguage() != null) { + koTimeConfig.setLanguage(config.getLanguage()); + } + return KoResult.success(); + } + +} diff --git a/src/main/java/cn/langpy/kotime/controller/KoCpuUsageController.java b/src/main/java/cn/langpy/kotime/controller/KoCpuUsageController.java new file mode 100644 index 0000000..314f65a --- /dev/null +++ b/src/main/java/cn/langpy/kotime/controller/KoCpuUsageController.java @@ -0,0 +1,31 @@ +package cn.langpy.kotime.controller; + +import cn.langpy.kotime.annotation.Auth; +import cn.langpy.kotime.model.KoResult; +import cn.langpy.kotime.service.metric.CpuMetricService; +import cn.langpy.kotime.service.core.SystemService; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.logging.Logger; + + +/** + * zhangchang + */ +@Controller +@RequestMapping("/koTime/cpus") +public class KoCpuUsageController { + private static Logger log = Logger.getLogger(KoCpuUsageController.class.toString()); + + @GetMapping("/usage") + @ResponseBody + @Auth + public KoResult usage() { + CpuMetricService instance = SystemService.getInstance(CpuMetricService.class); + return KoResult.success(instance.getCpuUsage()); + } + +} diff --git a/src/main/java/cn/langpy/kotime/controller/KoDynamicPropertyController.java b/src/main/java/cn/langpy/kotime/controller/KoDynamicPropertyController.java new file mode 100644 index 0000000..738d744 --- /dev/null +++ b/src/main/java/cn/langpy/kotime/controller/KoDynamicPropertyController.java @@ -0,0 +1,65 @@ +package cn.langpy.kotime.controller; + +import cn.langpy.kotime.annotation.Auth; +import cn.langpy.kotime.model.*; + +import cn.langpy.kotime.util.Context; +import org.springframework.stereotype.Controller; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.*; +import java.util.logging.Logger; + + +/** + * zhangchang + */ +@Controller +@RequestMapping("/koTime/dynamicProperties") +public class KoDynamicPropertyController { + private static Logger log = Logger.getLogger(KoDynamicPropertyController.class.toString()); + + + @PutMapping + @ResponseBody + @Auth + public KoResult updateDynamicProperties(@RequestBody TextParam textParam) { + if (!StringUtils.hasText(textParam.getText())) { + return KoResult.failed("更新失败"); + } + String[] textSplit = textParam.getText().trim().split("\n"); + Properties dynamicProperties = Context.getDynamicProperties(); + for (String line : textSplit) { + line = line.trim(); + if (line.length()==0 || line.startsWith("#") || line.startsWith("//")) { + continue; + } + int i = line.indexOf("="); + if (i<1) { + continue; + } + String propertyStr = line.substring(0, i).trim(); + String valueStr = line.substring(i+1).trim(); + log.info("updated property: "+propertyStr+"=("+dynamicProperties.get(propertyStr)+"->"+valueStr+")"); + dynamicProperties.setProperty(propertyStr,valueStr); + } + + return KoResult.success(); + } + + @GetMapping + @ResponseBody + @Auth + public KoResult getDynamicProperties() { + Properties dynamicProperties = Context.getDynamicProperties(); + StringBuilder stringBuilder = new StringBuilder(); + for (String key : dynamicProperties.stringPropertyNames()) { + String value = dynamicProperties.getProperty(key); + if (value!=null) { + stringBuilder.append(key+"="+value+"\n"); + } + } + return KoResult.success(stringBuilder.toString()); + } +} diff --git a/src/main/java/cn/langpy/kotime/controller/KoExceptionController.java b/src/main/java/cn/langpy/kotime/controller/KoExceptionController.java new file mode 100644 index 0000000..0399ad7 --- /dev/null +++ b/src/main/java/cn/langpy/kotime/controller/KoExceptionController.java @@ -0,0 +1,44 @@ +package cn.langpy.kotime.controller; + +import cn.langpy.kotime.annotation.Auth; +import cn.langpy.kotime.model.*; +import cn.langpy.kotime.service.GraphService; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + + +import java.util.List; +import java.util.logging.Logger; + + +/** + * zhangchang + */ +@Controller +@RequestMapping("/koTime/exceptions") +public class KoExceptionController { + private static Logger log = Logger.getLogger(KoExceptionController.class.toString()); + + + @GetMapping + @ResponseBody + @Auth + public KoResult getExceptions() { + GraphService graphService = GraphService.getInstance(); + List exceptionList = graphService.getExceptions(); + return KoResult.success(exceptionList); + } + + @GetMapping("/{exceptionId}/details") + @ResponseBody + @Auth + public KoResult getMethodsByExceptionId(@PathVariable("exceptionId") String exceptionId, String message) { + GraphService graphService = GraphService.getInstance(); + List exceptionInfos = graphService.getExceptionInfos(exceptionId, message); + return KoResult.success(exceptionInfos); + } + +} diff --git a/src/main/java/cn/langpy/kotime/controller/KoGcUsageController.java b/src/main/java/cn/langpy/kotime/controller/KoGcUsageController.java new file mode 100644 index 0000000..742a0b1 --- /dev/null +++ b/src/main/java/cn/langpy/kotime/controller/KoGcUsageController.java @@ -0,0 +1,31 @@ +package cn.langpy.kotime.controller; + +import cn.langpy.kotime.annotation.Auth; +import cn.langpy.kotime.model.KoResult; +import cn.langpy.kotime.service.metric.GcMetricService; +import cn.langpy.kotime.service.core.SystemService; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.logging.Logger; + + +/** + * zhangchang + */ +@Controller +@RequestMapping("/koTime/gcs") +public class KoGcUsageController { + private static Logger log = Logger.getLogger(KoGcUsageController.class.toString()); + + @GetMapping("/usage") + @ResponseBody + @Auth + public KoResult usage() { + GcMetricService instance = SystemService.getInstance(GcMetricService.class); + return KoResult.success(instance.getGcUsage()); + } + +} diff --git a/src/main/java/cn/langpy/kotime/controller/KoInitController.java b/src/main/java/cn/langpy/kotime/controller/KoInitController.java index cf48ca9..d0a4bb4 100644 --- a/src/main/java/cn/langpy/kotime/controller/KoInitController.java +++ b/src/main/java/cn/langpy/kotime/controller/KoInitController.java @@ -1,7 +1,9 @@ package cn.langpy.kotime.controller; +import cn.langpy.kotime.annotation.Auth; import cn.langpy.kotime.constant.KoConstant; import cn.langpy.kotime.model.*; +import cn.langpy.kotime.service.GraphService; import cn.langpy.kotime.util.Context; import cn.langpy.kotime.util.InvalidAuthInfoException; import cn.langpy.kotime.util.KoUtil; @@ -44,26 +46,22 @@ public class KoInitController { @PostMapping("/login") @ResponseBody - public Map login(@RequestBody UserInfo userInfo) { + public KoResult login(@RequestBody UserInfo userInfo) { if (null == userInfo || !StringUtils.hasText(userInfo.getUserName()) || !StringUtils.hasText(userInfo.getPassword())) { throw new InvalidAuthInfoException("failed to login for kotime,please fill userName and password!"); } - Map map = new HashMap(); if (userName.equals(userInfo.getUserName()) && password.equals(userInfo.getPassword())) { String token = KoUtil.login(userInfo.getUserName()); - map.put("state", 1); + Map map = new HashMap(); map.put("token", token); - return map; + return KoResult.success(map); } - map.put("state", 0); - return map; + return KoResult.failed("登录失败"); } @GetMapping("/isLogin") @ResponseBody - public Map isLogin(String kotoken) { - Map map = new HashMap(); - map.put("state", 1); + public KoResult isLogin(String kotoken) { boolean checkLogin = false; if (StringUtils.hasText(kotoken)) { if (kotoken.equals(Context.getConfig().getStaticToken())) { @@ -72,8 +70,18 @@ public class KoInitController { checkLogin = KoUtil.isLogin(kotoken); } } + Map map = new HashMap(); map.put("isLogin", checkLogin ? 1 : 0); - return map; + return KoResult.success(map); + } + + @PostMapping("/clearData") + @ResponseBody + @Auth + public KoResult clearData() { + GraphService graphService = GraphService.getInstance(); + graphService.clearAll(); + return KoResult.success(); } diff --git a/src/main/java/cn/langpy/kotime/controller/KoInterfaceController.java b/src/main/java/cn/langpy/kotime/controller/KoInterfaceController.java new file mode 100644 index 0000000..5f00c88 --- /dev/null +++ b/src/main/java/cn/langpy/kotime/controller/KoInterfaceController.java @@ -0,0 +1,118 @@ +package cn.langpy.kotime.controller; + +import cn.langpy.kotime.annotation.Auth; +import cn.langpy.kotime.model.KoResult; +import cn.langpy.kotime.model.MethodInfo; +import cn.langpy.kotime.model.ParamMetric; +import cn.langpy.kotime.model.SystemStatistic; +import cn.langpy.kotime.service.GraphService; +import cn.langpy.kotime.util.Context; +import org.springframework.stereotype.Controller; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.*; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + + +/** + * zhangchang + */ +@Controller +@RequestMapping("/koTime/interfaces") +public class KoInterfaceController { + private static Logger log = Logger.getLogger(KoInterfaceController.class.toString()); + + @GetMapping("/usage") + @ResponseBody + @Auth + public KoResult usage() { + GraphService graphService = GraphService.getInstance(); + SystemStatistic system = graphService.getRunStatistic(); + return KoResult.success(system); + } + + @GetMapping + @ResponseBody + @Auth + public KoResult> getInterfaces(String question, String orderBy, String sort) { + GraphService graphService = GraphService.getInstance(); + List list = null; + if (StringUtils.hasText(question)) { + list = graphService.searchMethods(question); + } else { + list = graphService.getControllers(); + } + + Collections.sort(list, (o1, o2) -> { + int sortValue = -1; + if ("asc".equals(sort)) { + sortValue = 1; + } + if ("callNum".equals(orderBy)) { + return o1.getCallNum().compareTo(o2.getCallNum()) * sortValue; + } else { + return o1.getAvgRunTime().compareTo(o2.getAvgRunTime()) * sortValue; + } + }); + return KoResult.success(list); + } + + @GetMapping("/export") + @ResponseBody + @Auth + public void export(String question, String orderBy, String sort, HttpServletResponse response) { + List apis = getInterfaces(question, orderBy, sort).getContent(); + response.setCharacterEncoding("utf-8"); + response.addHeader("Content-Disposition", "attachment; filename=interfaces.csv"); + + try (OutputStream out = response.getOutputStream(); + BufferedOutputStream bufferedOut = new BufferedOutputStream(out)) { + String line = "序号,类名,方法名,路由,平均响应(ms),调用次数\n"; + if ("english".equals(Context.getConfig().getLanguage())) { + line = "No,ClassName,Method,Route,Avg(ms),CallNum\n"; + } + bufferedOut.write(line.getBytes("utf-8")); + for (int i = 0; i < apis.size(); i++) { + MethodInfo methodInfo = apis.get(i); + line = (i + 1) + "," + methodInfo.getClassName() + "," + methodInfo.getMethodName() + "()," + methodInfo.getRouteName() + "," + methodInfo.getAvgRunTime() + "," + methodInfo.getCallNum() + "\n"; + bufferedOut.write(line.getBytes("utf-8")); + } + bufferedOut.flush(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @GetMapping("/searchCondidate") + @ResponseBody + @Auth + public KoResult> searchCondidate(String question) { + GraphService graphService = GraphService.getInstance(); + List list = graphService.getCondidates(question); + return KoResult.success(list); + } + + @GetMapping("/{methodId}/paramMetric") + @ResponseBody + @Auth + public KoResult> paramMetric(@PathVariable("methodId") String methodId) { + GraphService graphService = GraphService.getInstance(); + Map list = graphService.getMethodParamGraph(methodId); + return KoResult.success(list); + } + + @GetMapping("/{methodId}/tree") + @ResponseBody + @Auth + public KoResult getTree(@PathVariable("methodId") String methodId) { + GraphService graphService = GraphService.getInstance(); + MethodInfo tree = graphService.getTree(methodId); + return KoResult.success(tree); + } + +} diff --git a/src/main/java/cn/langpy/kotime/controller/KoJvmSpaceController.java b/src/main/java/cn/langpy/kotime/controller/KoJvmSpaceController.java new file mode 100644 index 0000000..78adb8b --- /dev/null +++ b/src/main/java/cn/langpy/kotime/controller/KoJvmSpaceController.java @@ -0,0 +1,57 @@ +package cn.langpy.kotime.controller; + +import cn.langpy.kotime.annotation.Auth; +import cn.langpy.kotime.model.KoResult; +import cn.langpy.kotime.service.metric.JvmSpaceMetricService; +import cn.langpy.kotime.service.core.SystemService; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + + +import java.util.logging.Logger; + + +/** + * zhangchang + */ +@Controller +@RequestMapping("/koTime/jvmSpaces") +public class KoJvmSpaceController { + private static Logger log = Logger.getLogger(KoJvmSpaceController.class.toString()); + + @GetMapping("/edenSpace") + @ResponseBody + @Auth + public KoResult edenSpace() { + JvmSpaceMetricService instance = SystemService.getInstance(JvmSpaceMetricService.class); + return KoResult.success(instance.getEdenSpaceInfo()); + } + + @GetMapping("/survivorSpace") + @ResponseBody + @Auth + public KoResult survivorSpace() { + JvmSpaceMetricService instance = SystemService.getInstance(JvmSpaceMetricService.class); + return KoResult.success(instance.getSurvivorSpaceInfo()); + } + + @GetMapping("/oldGen") + @ResponseBody + @Auth + public KoResult oldGen() { + JvmSpaceMetricService instance = SystemService.getInstance(JvmSpaceMetricService.class); + return KoResult.success(instance.getOldGenInfo()); + } + + @GetMapping("/metaspace") + @ResponseBody + @Auth + public KoResult metaspace() { + JvmSpaceMetricService instance = SystemService.getInstance(JvmSpaceMetricService.class); + return KoResult.success(instance.getMetaspaceInfo()); + } + + +} diff --git a/src/main/java/cn/langpy/kotime/controller/KoMemoryController.java b/src/main/java/cn/langpy/kotime/controller/KoMemoryController.java new file mode 100644 index 0000000..a62b5b2 --- /dev/null +++ b/src/main/java/cn/langpy/kotime/controller/KoMemoryController.java @@ -0,0 +1,87 @@ +package cn.langpy.kotime.controller; + +import cn.langpy.kotime.annotation.Auth; +import cn.langpy.kotime.model.*; +import cn.langpy.kotime.service.core.SystemService; +import cn.langpy.kotime.service.metric.MemoryMetricService; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + + +import jakarta.servlet.http.HttpServletResponse; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.FileInputStream; +import java.io.OutputStream; +import java.util.logging.Logger; + + +/** + * zhangchang + */ +@Controller +@RequestMapping("/koTime/memories") +public class KoMemoryController { + private static Logger log = Logger.getLogger(KoMemoryController.class.toString()); + + @GetMapping("/heap") + @ResponseBody + @Auth + public KoResult heap() { + MemoryMetricService instance = SystemService.getInstance(MemoryMetricService.class); + JvmMemoryInfo heapMemoryInfo = instance.getHeapMemoryInfo(); + return KoResult.success(heapMemoryInfo); + } + + @GetMapping("/nonHeap") + @ResponseBody + @Auth + public KoResult nonHeap() { + MemoryMetricService instance = SystemService.getInstance(MemoryMetricService.class); + JvmMemoryInfo heapMemoryInfo = instance.getNonHeapMemoryInfo(); + return KoResult.success(heapMemoryInfo); + } + + @GetMapping("/physical") + @ResponseBody + @Auth + public KoResult physical() { + MemoryMetricService instance = SystemService.getInstance(MemoryMetricService.class); + PhysicalMemoryInfo heapMemoryInfo = instance.getPhysicalMemoryInfo(); + return KoResult.success(heapMemoryInfo); + } + + @GetMapping("/heap/export") + @ResponseBody + @Auth + public void heapExport(Boolean live, HttpServletResponse response) { + live = live == null ? false : live; + MemoryMetricService instance = SystemService.getInstance(MemoryMetricService.class); + String heapDumpFile = instance.getHeapDumpFile(live); + if (heapDumpFile == null) { + throw new RuntimeException("Can not dumpheap file!"); + } + log.info(heapDumpFile); + response.setCharacterEncoding("utf-8"); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + instance.getHeapDumpFileName(live)); + try (OutputStream out = response.getOutputStream(); + BufferedOutputStream bufferedOut = new BufferedOutputStream(out); + FileInputStream fileInputStream = new FileInputStream(heapDumpFile); + BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream)) { + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = bufferedInputStream.read(buffer)) != -1) { + bufferedOut.write(buffer, 0, bytesRead); + } + bufferedOut.flush(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + +} diff --git a/src/main/java/cn/langpy/kotime/controller/KoThreadController.java b/src/main/java/cn/langpy/kotime/controller/KoThreadController.java new file mode 100644 index 0000000..b685b8f --- /dev/null +++ b/src/main/java/cn/langpy/kotime/controller/KoThreadController.java @@ -0,0 +1,62 @@ +package cn.langpy.kotime.controller; + +import cn.langpy.kotime.annotation.Auth; +import cn.langpy.kotime.model.KoResult; +import cn.langpy.kotime.model.ThreadInfo; +import cn.langpy.kotime.service.core.SystemService; +import cn.langpy.kotime.service.metric.ThreadMetricService; +import org.springframework.stereotype.Controller; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import static cn.langpy.kotime.model.ThreadInfo.COMPARATOR; + + +/** + * zhangchang + */ +@Controller +@RequestMapping("/koTime/threads") +public class KoThreadController { + private static Logger log = Logger.getLogger(KoThreadController.class.toString()); + + @GetMapping("/usage") + @ResponseBody + @Auth + public KoResult usage() { + ThreadMetricService instance = SystemService.getInstance(ThreadMetricService.class); + return KoResult.success(instance.getThreadUsage()); + } + + @GetMapping + @ResponseBody + @Auth + public KoResult threads(String state) { + ThreadMetricService instance = SystemService.getInstance(ThreadMetricService.class); + List threads = instance.getThreads(); + threads = threads.stream().sorted(COMPARATOR).collect(Collectors.toList()); + + Map stateCounting = threads.stream().collect(Collectors.groupingBy(ThreadInfo::getState, Collectors.counting())); + stateCounting.put("all", (long) threads.size()); + + Map map = new HashMap(); + map.put("statistics", stateCounting); + if (StringUtils.hasText(state)) { + threads = threads.stream().filter(a -> a.getState().equals(state)).collect(Collectors.toList()); + } + map.put("threads", threads); + return KoResult.success(map); + } + + + +} diff --git a/src/main/java/cn/langpy/kotime/controller/KoTimeController.java b/src/main/java/cn/langpy/kotime/controller/KoTimeController.java deleted file mode 100644 index 3ed25fb..0000000 --- a/src/main/java/cn/langpy/kotime/controller/KoTimeController.java +++ /dev/null @@ -1,376 +0,0 @@ -package cn.langpy.kotime.controller; - -import cn.langpy.kotime.annotation.Auth; -import cn.langpy.kotime.config.DefaultConfig; -import cn.langpy.kotime.model.*; -import cn.langpy.kotime.service.*; -import cn.langpy.kotime.util.Context; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Controller; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import jakarta.servlet.http.HttpServletResponse; -import java.io.*; -import java.util.*; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -import static cn.langpy.kotime.model.ThreadInfo.COMPARATOR; - -/** - * zhangchang - */ -@Controller -@RequestMapping("/koTime") -public class KoTimeController { - private static Logger log = Logger.getLogger(KoTimeController.class.toString()); - - - @GetMapping("/getConfig") - @ResponseBody - @Auth - public DefaultConfig getConfig() { - DefaultConfig config = Context.getConfig(); - return config; - } - - @GetMapping("/getStatistic") - @ResponseBody - @Auth - public SystemStatistic getStatistic() { - GraphService graphService = GraphService.getInstance(); - SystemStatistic system = graphService.getRunStatistic(); - return system; - } - - @GetMapping("/getApis") - @ResponseBody - @Auth - public List getApis(String question,String orderBy,String sort) { - GraphService graphService = GraphService.getInstance(); - List list = null; - if (StringUtils.hasText(question)) { - list = graphService.searchMethods(question); - } else { - list = graphService.getControllers(); - } - - Collections.sort(list, (o1, o2) -> { - int sortValue = -1; - if ("asc".equals(sort)) { - sortValue = 1; - } - if ("callNum".equals(orderBy)) { - return o1.getCallNum().compareTo(o2.getCallNum())* sortValue; - }else { - return o1.getAvgRunTime().compareTo(o2.getAvgRunTime())* sortValue; - } - }); - return list; - } - - @GetMapping("/exportApis") - @ResponseBody - @Auth - public void exportApis(String question, String orderBy, String sort, HttpServletResponse response) { - List apis = getApis(question, orderBy, sort); - response.setCharacterEncoding("utf-8"); - response.addHeader("Content-Disposition", "attachment; filename=interfaces.csv"); - - try( OutputStream out = response.getOutputStream(); - BufferedOutputStream bufferedOut = new BufferedOutputStream(out)){ - String line = "序号,类名,方法名,路由,平均响应(ms),调用次数\n"; - if ("english".equals(Context.getConfig().getLanguage())) { - line = "No,ClassName,Method,Route,Avg(ms),CallNum\n"; - } - bufferedOut.write(line.getBytes("utf-8")); - for (int i = 0; i < apis.size(); i++) { - MethodInfo methodInfo = apis.get(i); - line = (i+1)+","+methodInfo.getClassName()+","+methodInfo.getMethodName()+"(),"+methodInfo.getRouteName()+","+methodInfo.getAvgRunTime()+","+methodInfo.getCallNum()+"\n"; - bufferedOut.write(line.getBytes("utf-8")); - } - bufferedOut.flush(); - }catch (Exception e){ - e.printStackTrace(); - } - } - - - @GetMapping("/getParamGraph") - @ResponseBody - @Auth - public Map getParamGraph(String methodId) { - GraphService graphService = GraphService.getInstance(); - Map list = graphService.getMethodParamGraph(methodId); - return list; - } - - @GetMapping("/getApiTips") - @ResponseBody - @Auth - public List getApiTips(String question) { - GraphService graphService = GraphService.getInstance(); - List list = graphService.getCondidates(question); - return list; - } - - - @GetMapping("/getExceptions") - @ResponseBody - @Auth - public List getExceptions() { - GraphService graphService = GraphService.getInstance(); - List exceptionList = graphService.getExceptions(); - return exceptionList; - } - - @GetMapping("/getTree") - @ResponseBody - @Auth - public MethodInfo getTree(String methodName) { - GraphService graphService = GraphService.getInstance(); - MethodInfo tree = graphService.getTree(methodName); - return tree; - } - - @GetMapping("/getMethodsByExceptionId") - @ResponseBody - @Auth - public List getMethodsByExceptionId(String exceptionId, String message) { - GraphService graphService = GraphService.getInstance(); - List exceptionInfos = graphService.getExceptionInfos(exceptionId, message); - return exceptionInfos; - } - - @PostMapping("/updateConfig") - @ResponseBody - @Auth - public boolean updateConfig(@RequestBody DefaultConfig config) { - DefaultConfig koTimeConfig = Context.getConfig(); - if (config.getEnable() != null) { - koTimeConfig.setEnable(config.getEnable()); - } - if (config.getExceptionEnable() != null) { - koTimeConfig.setExceptionEnable(config.getExceptionEnable()); - } - if (config.getLogEnable() != null) { - koTimeConfig.setLogEnable(config.getLogEnable()); - } - if (config.getMailEnable() != null) { - koTimeConfig.setMailEnable(config.getMailEnable()); - } - if (config.getAbbreviationEnable() != null) { - koTimeConfig.setAbbreviationEnable(config.getAbbreviationEnable()); - } - if (config.getThreshold() != null) { - koTimeConfig.setThreshold(config.getThreshold()); - } - if (config.getLanguage() != null) { - koTimeConfig.setLanguage(config.getLanguage()); - } - return true; - } - - @PostMapping("/updateClass") - @ResponseBody - @Auth - public Map updateClass(@RequestParam("classFile") MultipartFile classFile, String className) { - Map map = new HashMap(); - if (classFile == null || classFile.isEmpty()) { - map.put("state", 0); - map.put("message", "文件不能为空"); - return map; - } - if (!StringUtils.hasText(className)) { - map.put("state", 0); - map.put("message", "类名不能为空"); - return map; - } - className = className.trim(); - File file = null; - try { - String originalFilename = classFile.getOriginalFilename(); - if (!originalFilename.endsWith(".class")) { - map.put("state", 0); - map.put("message", "仅支持.class文件"); - return map; - } - String[] filename = originalFilename.split("\\."); - String substring = className.substring(className.lastIndexOf(".") + 1); - if (!substring.equals(filename[0])) { - map.put("state", 0); - map.put("message", "请确认类名是否正确"); - return map; - } - file = uploadFile(classFile.getBytes(), filename[0]); - } catch (IOException e) { - log.severe("Error class file!"); - map.put("state", 0); - map.put("message", "无法解析文件"); - return map; - } - final ClassService classService = ClassService.getInstance(); - classService.updateClass(className, file.getAbsolutePath()); - file.deleteOnExit(); - - map.put("state", 1); - map.put("message", "更新成功"); - return map; - } - - - private static File uploadFile(byte[] file, String fileName) throws IOException { - FileOutputStream out = null; - try { - File targetFile = File.createTempFile(fileName, ".class", new File(System.getProperty("java.io.tmpdir"))); - out = new FileOutputStream(targetFile.getAbsolutePath()); - out.write(file); - out.flush(); - return targetFile; - } catch (Exception e) { - log.severe("" + e); - } finally { - if (out != null) { - out.flush(); - out.close(); - } - } - return null; - } - - @GetMapping("/getCpuInfo") - @ResponseBody - @Auth - public CpuInfo getCpuInfo() { - SysUsageService usageService = SysUsageService.newInstance(); - CpuInfo cpuInfo = usageService.getCpuInfo(); - return cpuInfo; - } - - @GetMapping("/getHeapMemoryInfo") - @ResponseBody - @Auth - public HeapMemoryInfo getHeapMemoryInfo() { - SysUsageService usageService = SysUsageService.newInstance(); - HeapMemoryInfo heapMemoryInfo = usageService.getHeapMemoryInfo(); - return heapMemoryInfo; - } - - - @GetMapping("/dumpHeap") - @ResponseBody -// @Auth - public void dumpHeap(Boolean live, HttpServletResponse response) { - live = live==null?false:live; - HeapDumpService heapDumpService = HeapDumpService.newInstance(); - String heapDumpFile = heapDumpService.getHeapDumpFile(live); - if (heapDumpFile == null) { - throw new RuntimeException("Can not dumpheap file!"); - } - log.info(heapDumpFile); - response.setCharacterEncoding("utf-8"); - response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); - response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="+heapDumpService.getHeapDumpFileName(live)); - try( OutputStream out = response.getOutputStream(); - BufferedOutputStream bufferedOut = new BufferedOutputStream(out); - FileInputStream fileInputStream = new FileInputStream(heapDumpFile); - BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream)){ - byte[] buffer = new byte[1024]; - int bytesRead; - while ((bytesRead = bufferedInputStream.read(buffer)) != -1) { - bufferedOut.write(buffer,0,bytesRead); - } - bufferedOut.flush(); - - }catch (Exception e){ - e.printStackTrace(); - } - } - - @GetMapping("/getPhysicalMemoryInfo") - @ResponseBody - @Auth - public PhysicalMemoryInfo getPhysicalMemoryInfo() { - SysUsageService usageService = SysUsageService.newInstance(); - PhysicalMemoryInfo physicalMemoryInfo = usageService.getPhysicalMemoryInfo(); - return physicalMemoryInfo; - } - - @PostMapping("/clearData") - @ResponseBody - @Auth - public boolean clearData() { - GraphService graphService = GraphService.getInstance(); - graphService.clearAll(); - return true; - } - - @GetMapping("/getThreadsInfo") - @ResponseBody - @Auth - public Map getThreadsInfo(String state) { - ThreadUsageService usageService = ThreadUsageService.newInstance(); - List threads = usageService.getThreads(); - threads = threads.stream().sorted(COMPARATOR).collect(Collectors.toList()); - - Map stateCounting = threads.stream().collect(Collectors.groupingBy(ThreadInfo::getState, Collectors.counting())); - stateCounting.put("all", (long) threads.size()); - - Map map = new HashMap(); - map.put("statistics", stateCounting); - if (StringUtils.hasText(state)) { - threads = threads.stream().filter(a -> a.getState().equals(state)).collect(Collectors.toList()); - } - map.put("threads", threads); - return map; - } - - @PostMapping("/updateDynamicProperties") - @ResponseBody - @Auth - public boolean updateDynamicProperties(@RequestBody TextParam textParam) { - if (!StringUtils.hasText(textParam.getText())) { - return false; - } - String[] textSplit = textParam.getText().trim().split("\n"); - Properties dynamicProperties = Context.getDynamicProperties(); - for (String line : textSplit) { - line = line.trim(); - if (line.length() == 0 || line.startsWith("#") || line.startsWith("//")) { - continue; - } - int i = line.indexOf("="); - if (i < 1) { - continue; - } - String propertyStr = line.substring(0, i).trim(); - String valueStr = line.substring(i + 1).trim(); - log.info("updated property: " + propertyStr + "=(" + dynamicProperties.get(propertyStr) + "->" + valueStr + ")"); - dynamicProperties.setProperty(propertyStr, valueStr); - } - - return true; - } - - @GetMapping("/getDynamicProperties") - @ResponseBody - @Auth - public Map getDynamicProperties() { - Map map = new HashMap(); - map.put("state", 0); - map.put("message", "文件不能为空"); - Properties dynamicProperties = Context.getDynamicProperties(); - StringBuilder stringBuilder = new StringBuilder(); - for (String key : dynamicProperties.stringPropertyNames()) { - String value = dynamicProperties.getProperty(key); - if (value != null) { - stringBuilder.append(key + "=" + value + "\n"); - } - } - map.put("data", stringBuilder.toString()); - return map; - } -} diff --git a/src/main/java/cn/langpy/kotime/data/DataBase.java b/src/main/java/cn/langpy/kotime/data/DataBase.java index de1c279..2d69995 100644 --- a/src/main/java/cn/langpy/kotime/data/DataBase.java +++ b/src/main/java/cn/langpy/kotime/data/DataBase.java @@ -14,6 +14,7 @@ import java.sql.Connection; import java.sql.SQLException; import java.util.*; import java.util.logging.Logger; +import java.util.stream.Collectors; import static java.util.stream.Collectors.toList; @@ -328,6 +329,16 @@ public class DataBase implements GraphService { systemStatistic.setMaxRunTime(max); systemStatistic.setMinRunTime(min); systemStatistic.setAvgRunTime(avg); + + + int maxCallNum = controllerApis.stream().map(api -> api.getCallNum()).max(Integer::compareTo).get(); + int minCallNum = controllerApis.stream().map(api -> api.getCallNum()).min(Integer::compareTo).get(); + double avgCallNum = controllerApis.stream().map(api -> api.getCallNum()).collect(Collectors.averagingInt(Integer::intValue)); + BigDecimal bgCallNum = BigDecimal.valueOf(avgCallNum); + avgCallNum = bgCallNum.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); + systemStatistic.setMaxCallNum(maxCallNum); + systemStatistic.setMinCallNum(minCallNum); + systemStatistic.setAvgCallNum(avgCallNum); return systemStatistic; } diff --git a/src/main/java/cn/langpy/kotime/data/MemoryBase.java b/src/main/java/cn/langpy/kotime/data/MemoryBase.java index 55f481f..93a1b5f 100644 --- a/src/main/java/cn/langpy/kotime/data/MemoryBase.java +++ b/src/main/java/cn/langpy/kotime/data/MemoryBase.java @@ -377,6 +377,15 @@ public class MemoryBase implements GraphService { systemStatistic.setMaxRunTime(max); systemStatistic.setMinRunTime(min); systemStatistic.setAvgRunTime(avg); + + int maxCallNum = controllerApis.stream().map(api -> api.getCallNum()).max(Integer::compareTo).get(); + int minCallNum = controllerApis.stream().map(api -> api.getCallNum()).min(Integer::compareTo).get(); + double avgCallNum = controllerApis.stream().map(api -> api.getCallNum()).collect(Collectors.averagingInt(Integer::intValue)); + BigDecimal bgCallNum = BigDecimal.valueOf(avgCallNum); + avgCallNum = bgCallNum.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); + systemStatistic.setMaxCallNum(maxCallNum); + systemStatistic.setMinCallNum(minCallNum); + systemStatistic.setAvgCallNum(avgCallNum); return systemStatistic; } diff --git a/src/main/java/cn/langpy/kotime/data/RedisBase.java b/src/main/java/cn/langpy/kotime/data/RedisBase.java index 2b2e544..8a8ab7c 100644 --- a/src/main/java/cn/langpy/kotime/data/RedisBase.java +++ b/src/main/java/cn/langpy/kotime/data/RedisBase.java @@ -411,6 +411,16 @@ public class RedisBase implements GraphService { systemStatistic.setMaxRunTime(max); systemStatistic.setMinRunTime(min); systemStatistic.setAvgRunTime(avg); + + + int maxCallNum = controllerApis.stream().map(api -> api.getCallNum()).max(Integer::compareTo).get(); + int minCallNum = controllerApis.stream().map(api -> api.getCallNum()).min(Integer::compareTo).get(); + double avgCallNum = controllerApis.stream().map(api -> api.getCallNum()).collect(Collectors.averagingInt(Integer::intValue)); + BigDecimal bgCallNum = BigDecimal.valueOf(avgCallNum); + avgCallNum = bgCallNum.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); + systemStatistic.setMaxCallNum(maxCallNum); + systemStatistic.setMinCallNum(minCallNum); + systemStatistic.setAvgCallNum(avgCallNum); return systemStatistic; } diff --git a/src/main/java/cn/langpy/kotime/model/ClassUsage.java b/src/main/java/cn/langpy/kotime/model/ClassUsage.java new file mode 100644 index 0000000..2878f8b --- /dev/null +++ b/src/main/java/cn/langpy/kotime/model/ClassUsage.java @@ -0,0 +1,31 @@ +package cn.langpy.kotime.model; + +public class ClassUsage { + private Long totalClassNum; + private Integer currentClassNum; + private Long unloadedClassNum; + + public Long getTotalClassNum() { + return totalClassNum; + } + + public void setTotalClassNum(Long totalClassNum) { + this.totalClassNum = totalClassNum; + } + + public Integer getCurrentClassNum() { + return currentClassNum; + } + + public void setCurrentClassNum(Integer currentClassNum) { + this.currentClassNum = currentClassNum; + } + + public Long getUnloadedClassNum() { + return unloadedClassNum; + } + + public void setUnloadedClassNum(Long unloadedClassNum) { + this.unloadedClassNum = unloadedClassNum; + } +} diff --git a/src/main/java/cn/langpy/kotime/model/CpuInfo.java b/src/main/java/cn/langpy/kotime/model/CpuUsage.java similarity index 97% rename from src/main/java/cn/langpy/kotime/model/CpuInfo.java rename to src/main/java/cn/langpy/kotime/model/CpuUsage.java index 7285945..fd8029a 100644 --- a/src/main/java/cn/langpy/kotime/model/CpuInfo.java +++ b/src/main/java/cn/langpy/kotime/model/CpuUsage.java @@ -1,6 +1,6 @@ package cn.langpy.kotime.model; -public class CpuInfo { +public class CpuUsage { private Double systemLoad; private Double userRate; private Double sysRate; diff --git a/src/main/java/cn/langpy/kotime/model/GarbageCollectionInfo.java b/src/main/java/cn/langpy/kotime/model/GarbageCollectionInfo.java new file mode 100644 index 0000000..1c92b55 --- /dev/null +++ b/src/main/java/cn/langpy/kotime/model/GarbageCollectionInfo.java @@ -0,0 +1,40 @@ +package cn.langpy.kotime.model; + +public class GarbageCollectionInfo { + private Integer youngGcNum; + private Long youngGcTime; + private Integer oldGcNum; + private Long oldGcNumTime; + + public Integer getYoungGcNum() { + return youngGcNum; + } + + public void setYoungGcNum(Integer youngGcNum) { + this.youngGcNum = youngGcNum; + } + + public Long getYoungGcTime() { + return youngGcTime; + } + + public void setYoungGcTime(Long youngGcTime) { + this.youngGcTime = youngGcTime; + } + + public Integer getOldGcNum() { + return oldGcNum; + } + + public void setOldGcNum(Integer oldGcNum) { + this.oldGcNum = oldGcNum; + } + + public Long getOldGcNumTime() { + return oldGcNumTime; + } + + public void setOldGcNumTime(Long oldGcNumTime) { + this.oldGcNumTime = oldGcNumTime; + } +} diff --git a/src/main/java/cn/langpy/kotime/model/GcUsage.java b/src/main/java/cn/langpy/kotime/model/GcUsage.java new file mode 100644 index 0000000..8bde706 --- /dev/null +++ b/src/main/java/cn/langpy/kotime/model/GcUsage.java @@ -0,0 +1,49 @@ +package cn.langpy.kotime.model; + +public class GcUsage { + private Long totalNum; + private Long minorNum; + private Long minorCostTime; + private Long fullNum; + private Long fullCostTime; + + public Long getTotalNum() { + return totalNum; + } + + public void setTotalNum(Long totalNum) { + this.totalNum = totalNum; + } + + public Long getMinorNum() { + return minorNum; + } + + public void setMinorNum(Long minorNum) { + this.minorNum = minorNum; + } + + public Long getMinorCostTime() { + return minorCostTime; + } + + public void setMinorCostTime(Long minorCostTime) { + this.minorCostTime = minorCostTime; + } + + public Long getFullNum() { + return fullNum; + } + + public void setFullNum(Long fullNum) { + this.fullNum = fullNum; + } + + public Long getFullCostTime() { + return fullCostTime; + } + + public void setFullCostTime(Long fullCostTime) { + this.fullCostTime = fullCostTime; + } +} diff --git a/src/main/java/cn/langpy/kotime/model/HeapMemoryInfo.java b/src/main/java/cn/langpy/kotime/model/JvmMemoryInfo.java similarity index 96% rename from src/main/java/cn/langpy/kotime/model/HeapMemoryInfo.java rename to src/main/java/cn/langpy/kotime/model/JvmMemoryInfo.java index 19afafe..eee2dc8 100644 --- a/src/main/java/cn/langpy/kotime/model/HeapMemoryInfo.java +++ b/src/main/java/cn/langpy/kotime/model/JvmMemoryInfo.java @@ -1,6 +1,6 @@ package cn.langpy.kotime.model; -public class HeapMemoryInfo { +public class JvmMemoryInfo { private Long initValue; private Long maxValue; private Long usedValue; diff --git a/src/main/java/cn/langpy/kotime/model/KoResult.java b/src/main/java/cn/langpy/kotime/model/KoResult.java new file mode 100644 index 0000000..a691cc4 --- /dev/null +++ b/src/main/java/cn/langpy/kotime/model/KoResult.java @@ -0,0 +1,58 @@ +package cn.langpy.kotime.model; + +/** + * zhangchang + * @param + */ +public class KoResult { + private Integer state = 1; + private String message; + private T content; + + + public static KoResult failed(String message) { + KoResult result = new KoResult(); + result.setState(0); + result.setMessage(message); + return result; + } + + public static KoResult success(Object content) { + KoResult result = new KoResult(); + result.setState(1); + result.setMessage("成功"); + result.setContent(content); + return result; + } + + public static KoResult success() { + KoResult result = new KoResult(); + result.setState(1); + result.setMessage("成功"); + return result; + } + + public Integer getState() { + return state; + } + + public void setState(Integer state) { + this.state = state; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public T getContent() { + return content; + } + + public void setContent(T content) { + this.content = content; + } +} diff --git a/src/main/java/cn/langpy/kotime/model/SystemStatistic.java b/src/main/java/cn/langpy/kotime/model/SystemStatistic.java index d7189f3..96a05ad 100644 --- a/src/main/java/cn/langpy/kotime/model/SystemStatistic.java +++ b/src/main/java/cn/langpy/kotime/model/SystemStatistic.java @@ -9,7 +9,9 @@ public class SystemStatistic { private Integer totalNum = 0 ; private Integer delayNum = 0; private Integer normalNum = 0; - + private Double avgCallNum = 0.0; + private Integer maxCallNum = 0; + private Integer minCallNum = 0; public String getName() { return name; } @@ -66,6 +68,30 @@ public class SystemStatistic { this.normalNum = normalNum; } + public Double getAvgCallNum() { + return avgCallNum; + } + + public void setAvgCallNum(Double avgCallNum) { + this.avgCallNum = avgCallNum; + } + + public Integer getMaxCallNum() { + return maxCallNum; + } + + public void setMaxCallNum(Integer maxCallNum) { + this.maxCallNum = maxCallNum; + } + + public Integer getMinCallNum() { + return minCallNum; + } + + public void setMinCallNum(Integer minCallNum) { + this.minCallNum = minCallNum; + } + @Override public String toString() { return "SystemStatistic{" + diff --git a/src/main/java/cn/langpy/kotime/model/ThreadInfo.java b/src/main/java/cn/langpy/kotime/model/ThreadInfo.java index 5b8b888..a5724ae 100644 --- a/src/main/java/cn/langpy/kotime/model/ThreadInfo.java +++ b/src/main/java/cn/langpy/kotime/model/ThreadInfo.java @@ -14,10 +14,19 @@ public class ThreadInfo { private String state; private Boolean isInterrupted; private Boolean isDaemon; + private Boolean deadLock; private Integer priority; private BigDecimal cpuUsage; private List stacks; + public Boolean getDeadLock() { + return this.deadLock; + } + + public void setDeadLock(Boolean deadLock) { + this.deadLock = deadLock; + } + public Long getId() { return id; } diff --git a/src/main/java/cn/langpy/kotime/model/ThreadUsage.java b/src/main/java/cn/langpy/kotime/model/ThreadUsage.java new file mode 100644 index 0000000..fcd348b --- /dev/null +++ b/src/main/java/cn/langpy/kotime/model/ThreadUsage.java @@ -0,0 +1,31 @@ +package cn.langpy.kotime.model; + +public class ThreadUsage { + private Integer totalNum; + private Integer runnableNum; + private Integer deadLockNum; + + public Integer getTotalNum() { + return totalNum; + } + + public void setTotalNum(Integer totalNum) { + this.totalNum = totalNum; + } + + public Integer getRunnableNum() { + return runnableNum; + } + + public void setRunnableNum(Integer runnableNum) { + this.runnableNum = runnableNum; + } + + public Integer getDeadLockNum() { + return deadLockNum; + } + + public void setDeadLockNum(Integer deadLockNum) { + this.deadLockNum = deadLockNum; + } +} diff --git a/src/main/java/cn/langpy/kotime/service/ClassService.java b/src/main/java/cn/langpy/kotime/service/ClassService.java deleted file mode 100644 index b9270f2..0000000 --- a/src/main/java/cn/langpy/kotime/service/ClassService.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.langpy.kotime.service; - - - -import java.util.logging.Logger; - -public interface ClassService { - void updateClass(String className, String classPath); - - static ClassService getInstance() { - return ClassServiceFactory.getInstance(); - } -} - -class ClassServiceFactory { - private static Logger log = Logger.getLogger(ClassServiceFactory.class.toString()); - - private static ClassService instance = null; - - public static ClassService getInstance() { - if (instance == null) { - synchronized (ClassServiceFactory.class) { - if (instance == null) { - instance = new JvmAttachClassService(); - } - } - } - return instance; - } -} - diff --git a/src/main/java/cn/langpy/kotime/service/HeapDumpService.java b/src/main/java/cn/langpy/kotime/service/HeapDumpService.java deleted file mode 100644 index c7115df..0000000 --- a/src/main/java/cn/langpy/kotime/service/HeapDumpService.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.langpy.kotime.service; - -import com.sun.management.HotSpotDiagnosticMXBean; - -import javax.management.MBeanServer; -import java.io.File; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.logging.Logger; - -public class HeapDumpService { - private static final String STANDARD_DUMP_NAME = "kotime-heapdump-%s.hprof"; - private static Logger log = Logger.getLogger(HeapDumpService.class.toString()); - private static final String HotSpotDiagnosticName = "com.sun.management:type=HotSpotDiagnostic"; - - public static HeapDumpService newInstance() { - return new HeapDumpService(); - } - - public String getHeapDumpFile(boolean live) { - String targetFile = System.getProperty("java.io.tmpdir")+File.separator+getHeapDumpFileName(live); - try { - MBeanServer server = ManagementFactory.getPlatformMBeanServer(); - HotSpotDiagnosticMXBean hotSpotDiagnostic = ManagementFactory.newPlatformMXBeanProxy(server, HotSpotDiagnosticName, HotSpotDiagnosticMXBean.class); - if (Files.exists(Paths.get(targetFile))) { - new File(targetFile).delete(); - } - hotSpotDiagnostic.dumpHeap(targetFile, live); - } catch (IOException e) { - e.printStackTrace(); - log.severe("Can not dump heap file!"); - } - return targetFile; - } - - public String getHeapDumpFileName(boolean live) { - if (live) { - return String.format(STANDARD_DUMP_NAME, "live"); - } - return String.format(STANDARD_DUMP_NAME, "all"); - } - -} diff --git a/src/main/java/cn/langpy/kotime/service/SysUsageService.java b/src/main/java/cn/langpy/kotime/service/SysUsageService.java deleted file mode 100644 index 7248bb2..0000000 --- a/src/main/java/cn/langpy/kotime/service/SysUsageService.java +++ /dev/null @@ -1,144 +0,0 @@ -package cn.langpy.kotime.service; - -import cn.langpy.kotime.model.CpuInfo; -import cn.langpy.kotime.model.HeapMemoryInfo; -import cn.langpy.kotime.model.PhysicalMemoryInfo; -import cn.langpy.kotime.util.Context; -import com.sun.management.OperatingSystemMXBean; -import oshi.SystemInfo; -import oshi.hardware.CentralProcessor; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.management.ManagementFactory; -import java.lang.management.MemoryMXBean; -import java.lang.management.MemoryUsage; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.logging.Logger; - -public class SysUsageService { - private static Logger log = Logger.getLogger(SysUsageService.class.toString()); - - public static SysUsageService newInstance() { - return new SysUsageService(); - } - - public CpuInfo getCpuInfo() { - SystemInfo systemInfo = new SystemInfo(); - CentralProcessor processor = systemInfo.getHardware().getProcessor(); - long[] prevTicks = processor.getSystemCpuLoadTicks(); - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - e.printStackTrace(); - } - long[] ticks = processor.getSystemCpuLoadTicks(); - long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - - prevTicks[CentralProcessor.TickType.NICE.getIndex()]; - - long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - - prevTicks[CentralProcessor.TickType.IRQ.getIndex()]; - - long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()]; - - long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - - prevTicks[CentralProcessor.TickType.STEAL.getIndex()]; - - long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()]; - - long user = ticks[CentralProcessor.TickType.USER.getIndex()] - - prevTicks[CentralProcessor.TickType.USER.getIndex()]; - - long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()]; - - long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - - prevTicks[CentralProcessor.TickType.IDLE.getIndex()]; - - long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; - CpuInfo cpuInfo = new CpuInfo(); - cpuInfo.setLogicalNum(processor.getLogicalProcessorCount()); - cpuInfo.setUserRate(user * 1.0 / totalCpu); - cpuInfo.setSysRate(cSys * 1.0 / totalCpu); - cpuInfo.setWaitRate(iowait * 1.0 / totalCpu); - cpuInfo.setSystemLoad(processor.getSystemCpuLoad(1000)); - return cpuInfo; - } - - public HeapMemoryInfo getHeapMemoryInfo() { - MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); - MemoryUsage memoryUsage = memoryMXBean.getHeapMemoryUsage(); - long initTotalMemorySize = memoryUsage.getInit(); - long maxMemorySize = memoryUsage.getMax(); - long usedMemorySize = memoryUsage.getUsed(); - HeapMemoryInfo heapMemoryInfo = new HeapMemoryInfo(); - heapMemoryInfo.setInitValue(initTotalMemorySize); - heapMemoryInfo.setMaxValue(maxMemorySize); - heapMemoryInfo.setUsedValue(usedMemorySize); - heapMemoryInfo.setUsedRate(usedMemorySize * 1.0 / maxMemorySize); - return heapMemoryInfo; - } - - public PhysicalMemoryInfo getPhysicalMemoryInfo() { - OperatingSystemMXBean osmxb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); - PhysicalMemoryInfo physicalMemoryInfo = new PhysicalMemoryInfo(); - physicalMemoryInfo.setInitValue(osmxb.getTotalPhysicalMemorySize()); - physicalMemoryInfo.setUsedValue(osmxb.getTotalPhysicalMemorySize() - osmxb.getFreePhysicalMemorySize()); - physicalMemoryInfo.setFreeValue(osmxb.getFreePhysicalMemorySize()); - physicalMemoryInfo.setUsedValue(physicalMemoryInfo.getInitValue() - physicalMemoryInfo.getFreeValue()); - physicalMemoryInfo.setUsedRate(physicalMemoryInfo.getUsedValue() * 1.0 / physicalMemoryInfo.getInitValue()); - if (isLinux()) { - Map processInfo = getProcessInfo(); - if (processInfo.containsKey("VmSize")) { - String VmRSSStr = processInfo.get("VmRSS"); - String VmSizeStr = VmRSSStr.split(" ")[0].trim(); - long VmRSS = Long.valueOf(VmSizeStr); - physicalMemoryInfo.setThisUsedValue(VmRSS*1024); - double rate = physicalMemoryInfo.getThisUsedValue()*1.0 / physicalMemoryInfo.getInitValue(); - physicalMemoryInfo.setThisUsedRate(rate); - } - } - return physicalMemoryInfo; - } - - public boolean isLinux() { - return System.getProperty("os.name").toLowerCase().contains("linux"); - } - - public Map getProcessInfo() { - Map processMetrics = new HashMap(); - Runtime runtime = Runtime.getRuntime(); - Process process = null; - try { - process = runtime.exec("cat /proc/" + Context.getPid() + "/status"); - } catch (IOException e) { - log.severe("Can not execute '"+"cat /proc/" + Context.getPid() + "/status"+"'"); - return processMetrics; - } - try (InputStream inputStream = process.getInputStream(); - InputStreamReader inputStreamReader = new InputStreamReader(inputStream); - BufferedReader bufferedReader = new BufferedReader(inputStreamReader) - ) { - String line =""; - while ((line = bufferedReader.readLine()) != null){ - String[] split = line.split(":"); - if (split.length==2) { - String key = split[0].trim(); - String value = split[1].trim(); - processMetrics.put(key,value); - } - } - } catch (Exception e) { - log.severe("Can not read the result of '"+"cat /proc/" + Context.getPid() + "/status"+"'"); - } - return processMetrics; - } - -} - diff --git a/src/main/java/cn/langpy/kotime/service/core/SystemService.java b/src/main/java/cn/langpy/kotime/service/core/SystemService.java new file mode 100644 index 0000000..54a1497 --- /dev/null +++ b/src/main/java/cn/langpy/kotime/service/core/SystemService.java @@ -0,0 +1,77 @@ +package cn.langpy.kotime.service.core; + + +import com.sun.management.HotSpotDiagnosticMXBean; + +import javax.management.MBeanServer; +import java.io.IOException; +import java.lang.management.*; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + +/** + * zhangchang + */ +public class SystemService { + Logger log = Logger.getLogger(SystemService.class.toString()); + private static final String HotSpotDiagnosticName = "com.sun.management:type=HotSpotDiagnostic"; + + final static Map serviceMap = new ConcurrentHashMap<>(); + + public static T getInstance(Class clazz) { + String simpleName = clazz.getSimpleName(); + T systemService = (T) serviceMap.get(simpleName); + if (systemService == null) { + synchronized (SystemService.class) { + if (!serviceMap.containsKey(simpleName)) { + try { + systemService = (T) clazz.newInstance(); + serviceMap.put(simpleName, systemService); + } catch (Exception e) { + e.printStackTrace(); + } + }else { + systemService = (T) serviceMap.get(simpleName); + } + } + } + return systemService; + } + + protected MemoryUsage getHeapMemoryUsage() { + return ManagementFactory.getMemoryMXBean().getHeapMemoryUsage(); + } + + protected MemoryUsage getNonHeapMemoryUsage() { + return ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage(); + } + + protected List getGarbageCollectorMXBeans() { + return ManagementFactory.getGarbageCollectorMXBeans(); + } + + protected List getMemoryPoolMXBeans() { + return ManagementFactory.getMemoryPoolMXBeans(); + } + + protected ThreadMXBean getThreadMXBean() { + return ManagementFactory.getThreadMXBean(); + } + + protected ClassLoadingMXBean getClassLoadingMXBean() { + return ManagementFactory.getClassLoadingMXBean(); + } + + public HotSpotDiagnosticMXBean getHotSpotDiagnosticMXBean() { + HotSpotDiagnosticMXBean hotSpotDiagnostic = null; + try { + MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + hotSpotDiagnostic = ManagementFactory.newPlatformMXBeanProxy(server, HotSpotDiagnosticName, HotSpotDiagnosticMXBean.class); + } catch (IOException e) { + e.printStackTrace(); + } + return hotSpotDiagnostic; + } +} diff --git a/src/main/java/cn/langpy/kotime/service/JvmAttachClassService.java b/src/main/java/cn/langpy/kotime/service/metric/ClassInfoService.java similarity index 81% rename from src/main/java/cn/langpy/kotime/service/JvmAttachClassService.java rename to src/main/java/cn/langpy/kotime/service/metric/ClassInfoService.java index d76ed30..37b03a1 100644 --- a/src/main/java/cn/langpy/kotime/service/JvmAttachClassService.java +++ b/src/main/java/cn/langpy/kotime/service/metric/ClassInfoService.java @@ -1,5 +1,7 @@ -package cn.langpy.kotime.service; +package cn.langpy.kotime.service.metric; +import cn.langpy.kotime.model.ClassUsage; +import cn.langpy.kotime.service.core.SystemService; import cn.langpy.kotime.util.Context; import net.bytebuddy.agent.VirtualMachine; import org.springframework.core.io.ClassPathResource; @@ -8,20 +10,28 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.management.ClassLoadingMXBean; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.logging.Logger; -public class JvmAttachClassService implements ClassService{ - private static Logger log = Logger.getLogger(JvmAttachClassService.class.toString()); +public class ClassInfoService extends SystemService { + Logger log = Logger.getLogger(ClassInfoService.class.toString()); + public ClassUsage getClassUsage() { + ClassUsage usage = new ClassUsage(); + ClassLoadingMXBean classLoadingMXBean = getClassLoadingMXBean(); + usage.setTotalClassNum(classLoadingMXBean.getTotalLoadedClassCount()); + usage.setCurrentClassNum(classLoadingMXBean.getLoadedClassCount()); + usage.setUnloadedClassNum(classLoadingMXBean.getUnloadedClassCount()); + return usage; + } private File agentJar; - public JvmAttachClassService() { + public ClassInfoService() { this.agentJar = createAgentJar(); } - @Override public void updateClass(String className, String classPath) { try { if (agentJar==null || !agentJar.exists()) { diff --git a/src/main/java/cn/langpy/kotime/service/metric/CpuMetricService.java b/src/main/java/cn/langpy/kotime/service/metric/CpuMetricService.java new file mode 100644 index 0000000..ec65f4c --- /dev/null +++ b/src/main/java/cn/langpy/kotime/service/metric/CpuMetricService.java @@ -0,0 +1,57 @@ +package cn.langpy.kotime.service.metric; + +import cn.langpy.kotime.model.CpuUsage; +import cn.langpy.kotime.service.core.SystemService; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; + +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +public class CpuMetricService extends SystemService { + Logger log = Logger.getLogger(CpuMetricService.class.toString()); + public CpuUsage getCpuUsage() { + SystemInfo systemInfo = new SystemInfo(); + CentralProcessor processor = systemInfo.getHardware().getProcessor(); + long[] prevTicks = processor.getSystemCpuLoadTicks(); + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + long[] ticks = processor.getSystemCpuLoadTicks(); + long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] + - prevTicks[CentralProcessor.TickType.NICE.getIndex()]; + + long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] + - prevTicks[CentralProcessor.TickType.IRQ.getIndex()]; + + long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] + - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()]; + + long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] + - prevTicks[CentralProcessor.TickType.STEAL.getIndex()]; + + long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] + - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()]; + + long user = ticks[CentralProcessor.TickType.USER.getIndex()] + - prevTicks[CentralProcessor.TickType.USER.getIndex()]; + + long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] + - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()]; + + long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] + - prevTicks[CentralProcessor.TickType.IDLE.getIndex()]; + + long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; + CpuUsage cpuInfo = new CpuUsage(); + cpuInfo.setLogicalNum(processor.getLogicalProcessorCount()); + cpuInfo.setUserRate(user * 1.0 / totalCpu); + cpuInfo.setSysRate(cSys * 1.0 / totalCpu); + cpuInfo.setWaitRate(iowait * 1.0 / totalCpu); + cpuInfo.setSystemLoad(processor.getSystemCpuLoad(1000)); + return cpuInfo; + } + +} diff --git a/src/main/java/cn/langpy/kotime/service/metric/GcMetricService.java b/src/main/java/cn/langpy/kotime/service/metric/GcMetricService.java new file mode 100644 index 0000000..4dcea4d --- /dev/null +++ b/src/main/java/cn/langpy/kotime/service/metric/GcMetricService.java @@ -0,0 +1,64 @@ +package cn.langpy.kotime.service.metric; + +import cn.langpy.kotime.model.GcUsage; +import cn.langpy.kotime.service.core.SystemService; + +import java.lang.management.GarbageCollectorMXBean; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Logger; + +public class GcMetricService extends SystemService { + private final static List youngMemoryPool = Arrays.asList("PS Eden Space", "PS Survivor Space"); + private final static List oldMemoryPool = Arrays.asList("PS Old Gen", "Metaspace"); + Logger log = Logger.getLogger(GcMetricService.class.toString()); + + public GcUsage getGcUsage() { + GcUsage gcUsage = new GcUsage(); + gcUsage.setFullNum(0L); + gcUsage.setMinorNum(0L); + gcUsage.setFullCostTime(0L); + gcUsage.setMinorCostTime(0L); + List garbageCollectorMXBeans = getGarbageCollectorMXBeans(); + for (GarbageCollectorMXBean bean : garbageCollectorMXBeans) { + if (isFullGc(bean)) { + gcUsage.setFullNum(gcUsage.getFullNum() + bean.getCollectionCount()); + gcUsage.setFullCostTime(gcUsage.getFullCostTime() + bean.getCollectionTime()); + } else if (isMinorGc(bean)) { + gcUsage.setMinorNum(gcUsage.getMinorNum() + bean.getCollectionCount()); + gcUsage.setMinorCostTime(gcUsage.getMinorCostTime() + bean.getCollectionTime()); + } else { + log.warning("kotime=>Can not recognize the garbage collector: " + bean); + } + } + gcUsage.setTotalNum(gcUsage.getFullNum() + gcUsage.getMinorNum()); + return gcUsage; + } + + private boolean isMinorGc(GarbageCollectorMXBean bean) { + String[] memoryPoolNames = bean.getMemoryPoolNames(); + boolean isMinor = false; + boolean isMajor = false; + for (String memoryPoolName : memoryPoolNames) { + if (youngMemoryPool.contains(memoryPoolName)) { + isMinor = true; + } + if (oldMemoryPool.contains(memoryPoolName)) { + isMajor = true; + } + } + return isMinor && !isMajor; + } + + private boolean isFullGc(GarbageCollectorMXBean bean) { + String[] memoryPoolNames = bean.getMemoryPoolNames(); + boolean isMajor = false; + for (String memoryPoolName : memoryPoolNames) { + if (oldMemoryPool.contains(memoryPoolName)) { + isMajor = true; + } + } + return isMajor; + } + +} diff --git a/src/main/java/cn/langpy/kotime/service/metric/JvmSpaceMetricService.java b/src/main/java/cn/langpy/kotime/service/metric/JvmSpaceMetricService.java new file mode 100644 index 0000000..1e81560 --- /dev/null +++ b/src/main/java/cn/langpy/kotime/service/metric/JvmSpaceMetricService.java @@ -0,0 +1,57 @@ +package cn.langpy.kotime.service.metric; + +import cn.langpy.kotime.model.JvmMemoryInfo; +import cn.langpy.kotime.service.core.SystemService; + + +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.MemoryUsage; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +/** + * zhangchang + */ +public class JvmSpaceMetricService extends SystemService { + Logger log = Logger.getLogger(JvmSpaceMetricService.class.toString()); + + public JvmMemoryInfo getEdenSpaceInfo() { + return getSpaceInfo("PS Eden Space"); + } + + public JvmMemoryInfo getSurvivorSpaceInfo() { + return getSpaceInfo("PS Survivor Space"); + } + + public JvmMemoryInfo getOldGenInfo() { + return getSpaceInfo("PS Old Gen"); + } + + public JvmMemoryInfo getMetaspaceInfo() { + return getSpaceInfo("Metaspace"); + } + + private JvmMemoryInfo getSpaceInfo(String name) { + List memoryPoolMXBeans = getMemoryPoolMXBeans(); + Map mxBeanMap = memoryPoolMXBeans.stream().collect(Collectors.toMap(v -> v.getName(), v -> v)); + MemoryPoolMXBean psEdenSpace = mxBeanMap.get(name); + JvmMemoryInfo heapMemoryInfo = new JvmMemoryInfo(); + if (psEdenSpace == null) { + heapMemoryInfo.setInitValue(0L); + heapMemoryInfo.setMaxValue(0L); + heapMemoryInfo.setUsedValue(0L); + heapMemoryInfo.setUsedRate(0.0); + return heapMemoryInfo; + } + MemoryUsage usage = psEdenSpace.getUsage(); + heapMemoryInfo.setInitValue(usage.getInit()); + heapMemoryInfo.setMaxValue(usage.getMax()); + heapMemoryInfo.setUsedValue(usage.getUsed()); + heapMemoryInfo.setUsedRate(usage.getUsed() * 1.0 / usage.getMax()); + return heapMemoryInfo; + } + + +} diff --git a/src/main/java/cn/langpy/kotime/service/metric/MemoryMetricService.java b/src/main/java/cn/langpy/kotime/service/metric/MemoryMetricService.java new file mode 100644 index 0000000..2ec0594 --- /dev/null +++ b/src/main/java/cn/langpy/kotime/service/metric/MemoryMetricService.java @@ -0,0 +1,127 @@ +package cn.langpy.kotime.service.metric; + +import cn.langpy.kotime.model.JvmMemoryInfo; +import cn.langpy.kotime.model.PhysicalMemoryInfo; +import cn.langpy.kotime.service.core.SystemService; +import cn.langpy.kotime.util.Context; +import com.sun.management.HotSpotDiagnosticMXBean; +import com.sun.management.OperatingSystemMXBean; + +import java.io.*; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryUsage; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +public class MemoryMetricService extends SystemService { + Logger log = Logger.getLogger(MemoryMetricService.class.toString()); + private static final String STANDARD_DUMP_NAME = "kotime-heapdump-%s.hprof"; + + public JvmMemoryInfo getHeapMemoryInfo() { + MemoryUsage memoryUsage = getHeapMemoryUsage(); + long initTotalMemorySize = memoryUsage.getInit(); + long maxMemorySize = memoryUsage.getMax(); + long usedMemorySize = memoryUsage.getUsed(); + JvmMemoryInfo heapMemoryInfo = new JvmMemoryInfo(); + heapMemoryInfo.setInitValue(initTotalMemorySize); + heapMemoryInfo.setMaxValue(maxMemorySize); + heapMemoryInfo.setUsedValue(usedMemorySize); + heapMemoryInfo.setUsedRate(usedMemorySize * 1.0 / maxMemorySize); + return heapMemoryInfo; + } + + public JvmMemoryInfo getNonHeapMemoryInfo() { + MemoryUsage memoryUsage = getNonHeapMemoryUsage(); + long initTotalMemorySize = memoryUsage.getInit(); + long maxMemorySize = memoryUsage.getMax(); + long usedMemorySize = memoryUsage.getUsed(); + JvmMemoryInfo heapMemoryInfo = new JvmMemoryInfo(); + heapMemoryInfo.setInitValue(initTotalMemorySize); + heapMemoryInfo.setMaxValue(maxMemorySize); + heapMemoryInfo.setUsedValue(usedMemorySize); + heapMemoryInfo.setUsedRate(usedMemorySize * 1.0 / maxMemorySize); + return heapMemoryInfo; + } + + public PhysicalMemoryInfo getPhysicalMemoryInfo() { + OperatingSystemMXBean osmxb = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + PhysicalMemoryInfo physicalMemoryInfo = new PhysicalMemoryInfo(); + physicalMemoryInfo.setInitValue(osmxb.getTotalPhysicalMemorySize()); + physicalMemoryInfo.setUsedValue(osmxb.getTotalPhysicalMemorySize() - osmxb.getFreePhysicalMemorySize()); + physicalMemoryInfo.setFreeValue(osmxb.getFreePhysicalMemorySize()); + physicalMemoryInfo.setUsedValue(physicalMemoryInfo.getInitValue() - physicalMemoryInfo.getFreeValue()); + physicalMemoryInfo.setUsedRate(physicalMemoryInfo.getUsedValue() * 1.0 / physicalMemoryInfo.getInitValue()); + if (isLinux()) { + Map processInfo = getProcessInfo(); + if (processInfo.containsKey("VmSize")) { + String VmRSSStr = processInfo.get("VmRSS"); + String VmSizeStr = VmRSSStr.split(" ")[0].trim(); + long VmRSS = Long.valueOf(VmSizeStr); + physicalMemoryInfo.setThisUsedValue(VmRSS * 1024); + double rate = physicalMemoryInfo.getThisUsedValue() * 1.0 / physicalMemoryInfo.getInitValue(); + physicalMemoryInfo.setThisUsedRate(rate); + } + } + return physicalMemoryInfo; + } + + public boolean isLinux() { + return System.getProperty("os.name").toLowerCase().contains("linux"); + } + + public Map getProcessInfo() { + Map processMetrics = new HashMap(); + Runtime runtime = Runtime.getRuntime(); + Process process = null; + try { + process = runtime.exec("cat /proc/" + Context.getPid() + "/status"); + } catch (IOException e) { + log.severe("Can not execute '" + "cat /proc/" + Context.getPid() + "/status" + "'"); + return processMetrics; + } + try (InputStream inputStream = process.getInputStream(); + InputStreamReader inputStreamReader = new InputStreamReader(inputStream); + BufferedReader bufferedReader = new BufferedReader(inputStreamReader) + ) { + String line = ""; + while ((line = bufferedReader.readLine()) != null) { + String[] split = line.split(":"); + if (split.length == 2) { + String key = split[0].trim(); + String value = split[1].trim(); + processMetrics.put(key, value); + } + } + } catch (Exception e) { + log.severe("Can not read the result of '" + "cat /proc/" + Context.getPid() + "/status" + "'"); + } + return processMetrics; + } + + public String getHeapDumpFile(boolean live) { + String targetFile = System.getProperty("java.io.tmpdir") + File.separator + getHeapDumpFileName(live); + try { + HotSpotDiagnosticMXBean hotSpotDiagnostic = getHotSpotDiagnosticMXBean(); + if (Files.exists(Paths.get(targetFile))) { + new File(targetFile).delete(); + } + hotSpotDiagnostic.dumpHeap(targetFile, live); + } catch (IOException e) { + e.printStackTrace(); + log.severe("Can not dump heap file!"); + } + return targetFile; + } + + public String getHeapDumpFileName(boolean live) { + if (live) { + return String.format(STANDARD_DUMP_NAME, "live"); + } + return String.format(STANDARD_DUMP_NAME, "all"); + } + + +} diff --git a/src/main/java/cn/langpy/kotime/service/ThreadUsageService.java b/src/main/java/cn/langpy/kotime/service/metric/ThreadMetricService.java similarity index 58% rename from src/main/java/cn/langpy/kotime/service/ThreadUsageService.java rename to src/main/java/cn/langpy/kotime/service/metric/ThreadMetricService.java index d778368..9cfa01f 100644 --- a/src/main/java/cn/langpy/kotime/service/ThreadUsageService.java +++ b/src/main/java/cn/langpy/kotime/service/metric/ThreadMetricService.java @@ -1,10 +1,9 @@ -package cn.langpy.kotime.service; +package cn.langpy.kotime.service.metric; import cn.langpy.kotime.model.ThreadInfo; -import cn.langpy.kotime.util.Context; -import org.springframework.util.CollectionUtils; +import cn.langpy.kotime.model.ThreadUsage; +import cn.langpy.kotime.service.core.SystemService; -import java.lang.management.ManagementFactory; import java.lang.management.ThreadMXBean; import java.math.BigDecimal; import java.util.ArrayList; @@ -15,19 +14,38 @@ import java.util.List; import java.util.logging.Logger; import java.util.stream.Collectors; -public class ThreadUsageService { - private static Logger log = Logger.getLogger(ThreadUsageService.class.toString()); +public class ThreadMetricService extends SystemService { + private static Logger log = Logger.getLogger(ThreadMetricService.class.toString()); - public static ThreadUsageService newInstance() { - return new ThreadUsageService(); + + public ThreadUsage getThreadUsage() { + ThreadUsage usage = new ThreadUsage(); + List threads = getThreads(); + usage.setTotalNum(threads.size()); + usage.setRunnableNum(getThreads("RUNNABLE").size()); + long[] deadlockedThreads = getThreadMXBean().findDeadlockedThreads(); + usage.setDeadLockNum(deadlockedThreads==null?0:deadlockedThreads.length); + return usage; + } + + public List getDeadlockThreadIds() { + List threads = new ArrayList<>(); + long[] deadlockedThreads = getThreadMXBean().findDeadlockedThreads(); + if (deadlockedThreads==null || deadlockedThreads.length == 0) { + return threads; + } + java.lang.management.ThreadInfo[] threadInfos = getThreadMXBean().getThreadInfo(deadlockedThreads); + List collect = Arrays.stream(threadInfos).map(a -> a.getThreadId()).collect(Collectors.toList()); + return collect; } public List getThreads() { ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); - ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();; + ThreadMXBean threadMXBean = getThreadMXBean(); int activeCount = threadGroup.activeCount(); Thread[] threads = new Thread[activeCount]; threadGroup.enumerate(threads); + List deadlockThreadIds = getDeadlockThreadIds(); List list = new ArrayList<>(); for (int i = 0; i < activeCount; i++) { Thread thread = threads[i]; @@ -48,6 +66,11 @@ public class ThreadUsageService { threadInfo.setPriority(thread.getPriority()); StackTraceElement[] stackTrace = thread.getStackTrace(); threadInfo.setStacks(Arrays.stream(stackTrace).collect(Collectors.toList())); + if (deadlockThreadIds.contains(thread.getId())) { + threadInfo.setDeadLock(true); + }else { + threadInfo.setDeadLock(false); + } list.add(threadInfo); } Collections.sort(list, Comparator.comparing(ThreadInfo::getCpuUsage).reversed()); diff --git a/src/main/resources/koapp.properties b/src/main/resources/koapp.properties index 13a0cd6..f18f9db 100644 --- a/src/main/resources/koapp.properties +++ b/src/main/resources/koapp.properties @@ -1 +1 @@ -ko-time.version=3.0.2 \ No newline at end of file +ko-time.version=3.1.0 \ No newline at end of file diff --git a/src/main/resources/kostatic/common.css b/src/main/resources/kostatic/common.css index 615405a..b3cfc8d 100644 --- a/src/main/resources/kostatic/common.css +++ b/src/main/resources/kostatic/common.css @@ -14,4 +14,56 @@ font-size: 14px; font-weight: 430; color: #3621a5; +} + +.metric-card { + border-radius: 10px; + background-color: #fefffe; + padding: 10px 20px 15px 20px; + text-align: left; +} + +.metric-card-summary-n { + font-size: 12px; + color: #1f2541; + justify-content: space-around; +} + +.metric-card-summary-v { + font-size: 18px; + color: #020718; + font-weight: bold; +} + +.metric-card-item { + text-align: left; + margin-top: -13px; + /*margin-left: -40px;*/ + list-style-type: none; +} + +.metric-card-item li { + line-height: 5px; +} + + +.metric-card-item-n { + font-size: 8px; + color: #3b3f4f; +} + +.metric-card-item-v { + font-size: 12px; + color: #020718; + font-weight: bold; +} + +.metric-card-item-point { + width: 4px; + height: 4px; + background-color: #11de71; + display: inline-block; + border-radius: 10px; + margin-right: 5px; + margin-bottom:1px; } \ No newline at end of file diff --git a/src/main/resources/kostatic/dict/chinese.properties b/src/main/resources/kostatic/dict/chinese.properties index 27b94b2..01ab3c4 100644 --- a/src/main/resources/kostatic/dict/chinese.properties +++ b/src/main/resources/kostatic/dict/chinese.properties @@ -10,8 +10,9 @@ tab.configuration=配置 tab.support=技术支持 tab.summary.interface-metric=接口统计 -tab.summary.response-metric=响应统计 -tab.summary.sysusage-metric=系统使用情况 +tab.summary.memory-metric=内存统计 +tab.summary.jvm-metric=JVM空间统计 +tab.summary.other-metric=其他指标 tab.summary.bottom-normal-tip=接口根据调用情况统计,未调用的接口无法被统计到,请先调用接口 tab.summary.bottom-close-tip=方法调用监测已关闭,数据将不会更新,需要开启请到配置面板 @@ -23,9 +24,21 @@ tab.summary.response-metric.avg-num=平均响应(ms) tab.summary.response-metric.max-num=最大响应(ms) tab.summary.response-metric.min-num=最小响应(ms) -tab.summary.sysusage-metric.cpu-usage=CPU使用率 +tab.summary.interface-metric.avg-call-num=平均调用数 +tab.summary.interface-metric.max-call-num=最大调用数 +tab.summary.interface-metric.min-call-num=最小调用数 + +tab.summary.sysusage-metric.cpu-usage=CPU tab.summary.sysusage-metric.heap-memory=堆内存 +tab.summary.sysusage-metric.non-heap-memory=非堆内存 tab.summary.sysusage-metric.physical-memory=物理内存 +tab.summary.sysusage-metric.eden-space=Eden +tab.summary.sysusage-metric.survivor-space=Survivor +tab.summary.sysusage-metric.old-gen=OldGen +tab.summary.sysusage-metric.meta-space=Metaspace +tab.summary.sysusage-metric.gc-num=GC次数 +tab.summary.sysusage-metric.thread-num=线程数 +tab.summary.sysusage-metric.loaded-class-num=加载类总数 tab.summary.sysusage-metric.cpu-usage.user-usage=用户使用率 tab.summary.sysusage-metric.cpu-usage.sys-usage=系统使用率 @@ -39,6 +52,14 @@ tab.summary.sysusage-metric.physical-memory.total=总物理内存 tab.summary.sysusage-metric.physical-memory.used=已使用内存 tab.summary.sysusage-metric.physical-memory.current-used=此程序占用 +tab.summary.sysusage-metric.gc-num.minor=Minor GC +tab.summary.sysusage-metric.gc-num.full=Major/Full GC + +tab.summary.sysusage-metric.thread-num.runnable=运行数 +tab.summary.sysusage-metric.thread-num.deadlock=死锁数 + +tab.summary.sysusage-metric.loaded-class-num.current=当前类总数 +tab.summary.sysusage-metric.loaded-class-num.unloaded=卸载类数 tab.interface.search-tip=搜索方法名或者类名... tab.interface.interface-list.avg-tip=平均响应 diff --git a/src/main/resources/kostatic/dict/english.properties b/src/main/resources/kostatic/dict/english.properties index c665533..1fab54c 100644 --- a/src/main/resources/kostatic/dict/english.properties +++ b/src/main/resources/kostatic/dict/english.properties @@ -9,9 +9,10 @@ tab.hotupdate=Hot Update tab.configuration=Configurations tab.support=Tech Support -tab.summary.interface-metric=Number of Interface -tab.summary.response-metric=Response Time -tab.summary.sysusage-metric=System Usage +tab.summary.interface-metric=Interface Indices +tab.summary.memory-metric=Memory Indices +tab.summary.jvm-metric=JVM Indices +tab.summary.other-metric=Other Indices tab.summary.bottom-normal-tip=Please call apis before visiting this page! tab.summary.bottom-close-tip=KoTime switch was closed! @@ -23,9 +24,21 @@ tab.summary.response-metric.avg-num=Avg(ms) tab.summary.response-metric.max-num=Max(ms) tab.summary.response-metric.min-num=Min(ms) -tab.summary.sysusage-metric.cpu-usage=CPU Usage +tab.summary.interface-metric.avg-call-num=Avg Call-Number +tab.summary.interface-metric.max-call-num=Max Call-Number +tab.summary.interface-metric.min-call-num=Min Call-Number + +tab.summary.sysusage-metric.cpu-usage=CPU tab.summary.sysusage-metric.heap-memory=Heap Memory +tab.summary.sysusage-metric.non-heap-memory=Non-Heap Memory tab.summary.sysusage-metric.physical-memory=Physical Memory +tab.summary.sysusage-metric.eden-space=Eden +tab.summary.sysusage-metric.survivor-space=Survivor +tab.summary.sysusage-metric.old-gen=OldGen +tab.summary.sysusage-metric.meta-space=Metaspace +tab.summary.sysusage-metric.gc-num=GC +tab.summary.sysusage-metric.thread-num=Thread Number +tab.summary.sysusage-metric.loaded-class-num=Loaded Class tab.summary.sysusage-metric.cpu-usage.user-usage=User Usage tab.summary.sysusage-metric.cpu-usage.sys-usage=System Usage @@ -39,6 +52,14 @@ tab.summary.sysusage-metric.physical-memory.total=Total tab.summary.sysusage-metric.physical-memory.used=Used tab.summary.sysusage-metric.physical-memory.current-used=ThisUsed +tab.summary.sysusage-metric.gc-num.minor=Minor GC +tab.summary.sysusage-metric.gc-num.full=Major/Full GC + +tab.summary.sysusage-metric.thread-num.runnable=Runnable +tab.summary.sysusage-metric.thread-num.deadlock=Deadlock + +tab.summary.sysusage-metric.loaded-class-num.current=Current +tab.summary.sysusage-metric.loaded-class-num.unloaded=Unloaded tab.interface.search-tip=search method name or class name... tab.interface.interface-list.avg-tip=avg diff --git a/src/main/resources/kostatic/operate-en.js b/src/main/resources/kostatic/operate-en.js deleted file mode 100644 index ee0c528..0000000 --- a/src/main/resources/kostatic/operate-en.js +++ /dev/null @@ -1,343 +0,0 @@ -function checkLogin() { - $.get(contextPath+'/koTime/isLogin?kotoken=' + globalToken+"&project="+globalProject, function (data) { - globalIsLogin = data['isLogin'] == 1 ? true : false; - }); - if (globalNeedLogin == true && globalIsLogin == false) { - UIkit.modal(document.getElementById("modal-login")).show(); - return; - } - ; -} - -function loadConfig(){ - $.get(contextPath+'/koTime/getConfig?kotoken='+globalToken+"&project="+globalProject, function (data) { - // let exceptionEnable = data['exceptionEnable']; - // - // let exceptionEnableDom = document.getElementById('exceptionEnable'); - // exceptionEnableDom.checked = exceptionEnable; - // - - let kotimeEnable = data['enable']; - // let kotimeEnableDom = document.getElementById('kotimeEnable'); - // kotimeEnableDom.checked = kotimeEnable; - - let apiTip = document.getElementById('apiTip'); - apiTip.innerHTML = kotimeEnable==true?"接口根据调用情况统计,未调用的接口无法被统计到,请先调用接口":"方法调用监测已关闭,数据将不会更新,需要开启请到配置面板"; - - let threshold = data['threshold']; - let timeThresholdDom = document.getElementById('timeThreshold'); - timeThresholdDom.value = threshold; - - // let logEnable = data['logEnable']; - // let logEnableDom = document.getElementById('logEnable'); - // logEnableDom.checked = logEnable; - - let language = data['language']; - $("#languageSwitch").val(language) - }); -} - -function addConfigEvent(){ - - - document.getElementById('languageSwitch').onchange = function(){ - let selectedObj = document.getElementById('languageSwitch'); - $.ajax({type:'POST',url:contextPath+'/koTime/updateConfig?kotoken='+globalToken+"&project="+globalProject,data:JSON.stringify({language:selectedObj.options[selectedObj.selectedIndex].value}),dataType:'json', headers: {'Content-Type': 'application/json' }}); - }; - - document.getElementById("timeThresholdYes").onclick = function(){ - $.ajax({type:'POST',url:contextPath+'/koTime/updateConfig?kotoken='+globalToken+"&project="+globalProject,data:JSON.stringify({threshold:document.getElementById('timeThreshold').value}),dataType:'json', headers: {'Content-Type': 'application/json' }}); - }; -} - -function loadStatistic(){ - $.get(contextPath+'/koTime/getStatistic?kotoken='+globalToken+"&project="+globalProject, function (data) { - let totalNum = data['totalNum']; - let systemTotalNum = document.getElementById("systemTotalNum"); - systemTotalNum.innerHTML=totalNum; - - let normalNum = data['normalNum']; - let systemNormalNum = document.getElementById("systemNormalNum"); - systemNormalNum.innerHTML=normalNum; - - let delayNum = data['delayNum']; - let systemDelayNum = document.getElementById("systemDelayNum"); - systemDelayNum.innerHTML=delayNum; - if (delayNum>0) { - document.getElementById("systemDelayNum-div").className+=' uk-label-danger'; - }else { - document.getElementById("systemDelayNum-div").className+=' uk-label-success'; - }; - - let avgRunTime = data['avgRunTime']; - let systemAvgRunTime = document.getElementById("systemAvgRunTime"); - systemAvgRunTime.innerHTML=avgRunTime; - if (avgRunTime>globalThreshold) { - document.getElementById("systemAvgRunTime-div").className+=' uk-label-danger'; - }else { - document.getElementById("systemAvgRunTime-div").className+=' uk-label-success'; - }; - - let maxRunTime = data['maxRunTime']; - let systemMaxRunTime = document.getElementById("systemMaxRunTime"); - systemMaxRunTime.innerHTML=maxRunTime; - if (maxRunTime>globalThreshold) { - document.getElementById("systemMaxRunTime-div").className+=' uk-label-danger'; - }else { - document.getElementById("systemMaxRunTime-div").className+=' uk-label-success'; - }; - - - let minRunTime = data['minRunTime']; - let systemMinRunTime = document.getElementById("systemMinRunTime"); - systemMinRunTime.innerHTML=minRunTime; - if (minRunTime>globalThreshold) { - document.getElementById("systemMinRunTime-div").className+=' uk-label-danger'; - }else { - document.getElementById("systemMinRunTime-div").className+=' uk-label-success'; - }; - - }); -} - -function loadApis(){ - let searchText = $("#searchText").val(); - $.get(contextPath+'/koTime/getApis?question='+searchText+'&kotoken='+globalToken+"&project="+globalProject, function (data) { - let element = document.getElementById('apiList'); - html = ''; - for (let i = 0; i < data.length; i++) { - let id = data[i]['id']; - let className = data[i]['className']; - let methodName = data[i]['methodName']; - let avgRunTime = data[i]['avgRunTime']; - let methodType = data[i]['methodType']; - let routeName = data[i]['routeName']; - - let apiId = className+"."+methodName; - let color = avgRunTime>globalThreshold?'danger':'success'; - if (methodType=='Controller' && routeName!=null && routeName!='') { - html += "
  • "+ className+"#"+methodName+" ("+routeName+")   avg "+avgRunTime+" ms
  • "; - }else{ - html += "
  • "+ className+"#"+methodName+"   avg "+avgRunTime+" ms
  • "; - } - }; - element.innerHTML = html; - }); -} - -function loadExceptions(){ - $.get(contextPath+'/koTime/getExceptions?kotoken='+globalToken+"&project="+globalProject, function (data) { - let element = document.getElementById('exceptionList'); - html = ''; - for (let i = 0; i < data.length; i++) { - let id = data[i]['id']; - let className = data[i]['className']; - let message = data[i]['message']; - html += "
  • "+className+"   "+message+"
  • "; - }; - element.innerHTML = html; - }); -} - -let methodParamMap = new Map(); -function paramGraph(e) { - let clickNode = e.currentTarget; - if (clickNode==null){ - return - }; - let clickNodeId = clickNode.id; - if (methodParamMap.has(clickNodeId+"ana")) { - graph.removeNode(clickNodeId+"ana") - methodParamMap.delete(clickNodeId+"ana") - }else { - $.get(contextPath+'/koTime/getParamGraph?kotoken='+globalToken+"&methodId="+clickNodeId.replace('node','')+"&project="+globalProject, function (data) { - let datas = [] - for(let key in data) { - datas.push( {"name":key+":avg "+data[key]['avgRunTime']+" ms"}) - }; - let paramGraphData = { - "id":clickNodeId+"ana", - "from":clickNodeId, - "title":{'name':"Combination of parameters"}, - "data":datas, - 'style':{ - 'title-color':'#427b72', - 'border-color':'#427b72', - 'data-font-size':'10px', - 'title-font-size':'12px' - } - } - let clickNodeX = Number(clickNode.getAttribute("x")); - let clickNodeY = Number(clickNode.getAttribute("y")); - graph.createNode(paramGraphData,clickNodeX,clickNodeY-100); - // graph.createNode(paramGraphData,e.x+150,e.y-30); - methodParamMap.set(clickNodeId+"ana","") - }); - } -} -function formData(data) { - if (data['avgRunTime']>globalThreshold) { - data['style'] = { - 'title-color':'#c71335', - 'border-color':'#c71335', - 'data-font-size':'10px', - 'title-font-size':'12px' - } - } - else{ - data['style'] = { - 'title-color':'#375d46', - 'border-color':'#375d46', - 'data-font-size':'10px', - 'title-font-size':'12px' - } - }; - data['title'] = {'name':data['name']}; - data['data'] = [ - {'name':'type:'+data['methodType']}, - {'name':'avg time:'+data['avgRunTime']+' ms'}, - {'name':'max time:'+data['maxRunTime']+' ms'}, - {'name':'min time:'+data['minRunTime']+' ms'} - ]; - if (data['exceptionNum']!=null && data['exceptionNum']>0) { - data['data'].push({'name':'number of exception:'+data['exceptionNum']+' 个'}); - } - data["dblclick"]="paramGraph"; - return data; -}; - - -function showMethods(name) { - let exceptionDetailDom = document.getElementById('layerDemo'); - exceptionDetailDom.innerHTML = ""; - let options = { - 'link-start-offsetx':8, - 'link-start-offsety':0, - 'link-end-offsetx':+10, - 'link-end-offsety':0, - 'link-width-offset':0 - }; - graph = new MetricFlow("layerDemo",options); - graph.threshold = globalThreshold; - UIkit.notification.closeAll(); - UIkit.modal(document.getElementById("modal-method")).show(); - - $.get(contextPath+'/koTime/getTree?methodName=' + name+'&kotoken='+globalToken+"&project="+globalProject, function (data) { - let rootX = 100; - let rootY = $(window).get(0).innerHeight / 2-50; - data['x'] = rootX; - data['y'] = rootY; - graph.createNodes(data,formData); - - }); - -}; - -function showExceptions(id,message) { - $.get(contextPath+'/koTime/getMethodsByExceptionId?exceptionId=' + id+'&message='+message+'&kotoken='+globalToken+"&project="+globalProject, function (data) { - let html = ''; - for (let i = 0; i < data.length; i++) { - html += - "
      \n" + - "\t
    • Class:"+data[i].occurClassName+"
    • \n" + - "\t
    • Method:"+data[i].methodName+"
    • \n" + - "\t
    • Line:"+data[i].location+"
    • \n" + - "\t
    • Exception message:"+data[i].message+"
    • \n" + - "
    " - if (data.length-1>i) { - html +='
    ' - } - }; - - let exceptionDetailDom = document.getElementById('exception-detail'); - exceptionDetailDom.innerHTML = html; - UIkit.notification.closeAll(); - UIkit.modal(document.getElementById("modal-exception")).show(); - }); -}; - -function login() { - let userId = $("#userName").val() - let password = $("#userPassword").val() - if (userId==undefined || userId.length<1 || password==undefined || password.length<1 ) { - UIkit.notification.closeAll() - UIkit.notification("请正确输入用户名和密码",{}); - return - } - $.ajax({ - type:'POST', - url:contextPath+"/koTime/login", - data:JSON.stringify({'userName':userId,'password':password}), - dataType:'json', - headers: {'Content-Type': 'application/json' }, - success:function (re) { - if (re['state']==1) { - UIkit.notification("Login was successful",{}); - UIkit.notification.closeAll() - sessionStorage.setItem("kotimeToken", re["token"]); - location.reload(); - }else { - UIkit.notification("Error user or password",{}); - } - }, - error:function (re) { - console.log(re) - } - }); -} -function searchTip(e){ - let question = $('#searchText').val() - $.get(contextPath+'/koTime/getApiTips?question='+question+'&kotoken='+globalToken+"&project="+globalProject, function (data) { - $("#condidates").html("") - for (let i = 0; i < data.length; i++) { - let name = data[i]; - $("#condidates").append(''); - for (let i = 0; i < data.length; i++) { - let name = data[i]; - $("#projects").append(``); - }; - }); -} - -function searchApis(e) { - if (e.keyCode == 13) { - let question = $('#searchText').val() - $.get(contextPath+'/koTime/getApis?question='+question+'&kotoken='+globalToken+"&project="+globalProject, function (data) { - let element = document.getElementById('apiList'); - html = ''; - for (let i = 0; i < data.length; i++) { - let id = data[i]['id']; - let className = data[i]['className']; - let methodName = data[i]['methodName']; - let avgRunTime = data[i]['avgRunTime']; - let methodType = data[i]['methodType']; - let routeName = data[i]['routeName']; - let apiId = className+"."+methodName; - let color = avgRunTime>globalThreshold?'danger':'success'; - - if (methodType=='Controller' && routeName!=null && routeName!='') { - html += "
  • "+ className+"#"+methodName+" ("+routeName+")   avg "+avgRunTime+" ms
  • "; - }else{ - html += "
  • "+ className+"#"+methodName+"   avg "+avgRunTime+" ms
  • "; - } - }; - element.innerHTML = html; - }); - $('#searchText').val(''); - } -} - -function getProjectName(){ - globalProject = document.querySelector("#projects").value; - loadStatistic(); - loadApis(); - loadExceptions(); -} \ No newline at end of file diff --git a/src/main/resources/kostatic/util.js b/src/main/resources/kostatic/util.js index 57e7e2b..b649d68 100644 --- a/src/main/resources/kostatic/util.js +++ b/src/main/resources/kostatic/util.js @@ -30,9 +30,26 @@ function post(url,data,successfun,errorFun) { }); } -function postFormData(url,data,successfun,errorFun) { +function put(url,data,successfun,errorFun) { fetch(url, { - method: 'post', + method: 'put', + body: JSON.stringify(data), + headers: { + 'Content-Type': 'application/json' + } + }).then(response => response.json()) + .then(json => { + successfun(json); + }).catch(e => { + if (errorFun) { + errorFun(e); + } + }); +} + +function putFormData(url,data,successfun,errorFun) { + fetch(url, { + method: 'put', body: data }).then(response => response.json()) .then(json => { diff --git a/src/main/resources/kotime.html b/src/main/resources/kotime.html index a187e2d..721e33e 100644 --- a/src/main/resources/kotime.html +++ b/src/main/resources/kotime.html @@ -35,7 +35,7 @@ function refreshData() { get(concatToken('contextPath/koTime/isLogin'), function (data) { - globalIsLogin = data['isLogin'] == 1 ? true : false; + globalIsLogin = data['content']['isLogin'] == 1 ? true : false; if (globalNeedLogin == true && globalIsLogin == false) { UIkit.modal(getDom("modal-login")).show(); return; @@ -47,7 +47,15 @@ loadExceptions(); loadCpuInfo(); loadHeapMemoryInfo(); + loadNonHeapMemoryInfo(); loadPhysicalMemoryInfo(); + loadEdenSpace(); + loadSurvivorSpace(); + loadOldGenSpace(); + loadMetaspace(); + loadGcUsage(); + loadThreadUsage(); + loadClassUsage(); loadThreadsInfo(); loadDynamicProperties(); } @@ -55,7 +63,12 @@ } function loadExceptions() { - get(concatToken('contextPath/koTime/getExceptions'), function (data) { + get(concatToken('contextPath/koTime/exceptions'), function (res) { + if (res['state']==0) { + noticeError("载入exceptions失败!"); + return; + } + let data = res['content']; let element = getDom('exceptionList'); html = ''; for (let i = 0; i < data.length; i++) { @@ -74,7 +87,12 @@ let searchText = searchDom.value; let apiSortName = getDom('apiSortName') let apiSortType = getDom('apiSortType') - get(concatToken(`contextPath/koTime/getApis?question=${searchText}&orderBy=${apiSortName.value}&sort=${apiSortType.value}`), function (data) { + get(concatToken(`contextPath/koTime/interfaces?question=${searchText}&orderBy=${apiSortName.value}&sort=${apiSortType.value}`), function (res) { + if (res['state']==0) { + noticeError("载入interfaces失败!"); + return; + } + let data = res['content']; searchDom.value=''; let element = getDom('apiList'); html = ''; @@ -104,7 +122,12 @@ } function loadStatistic() { - get(concatToken('contextPath/koTime/getStatistic'), function (data) { + get(concatToken('contextPath/koTime/interfaces/usage'), function (res) { + if (res['state']==0) { + noticeError("载入interfaces/usage失败!"); + return; + } + let data = res['content']; let totalNum = data['totalNum']; let systemTotalNum = getDom("systemTotalNum"); systemTotalNum.innerHTML = totalNum; @@ -145,15 +168,27 @@ getDom("systemMinRunTime").style.color = '#cc0c0c'; } else { getDom("systemMinRunTime").style.color = '#29da93'; - } - ; + }; + + let avgCallNum = data['avgCallNum']; + let systemAvgCallNumDom = getDom("systemAvgCallNum"); + systemAvgCallNumDom.innerHTML = avgCallNum; + + let minCallNum = data['minCallNum']; + let systemMinCallNumDom = getDom("systemMinCallNum"); + systemMinCallNumDom.innerHTML = minCallNum; + + + let maxCallNum = data['maxCallNum']; + let systemMaxCallNumDom = getDom("systemMaxCallNum"); + systemMaxCallNumDom.innerHTML = maxCallNum; }); } function addConfigEvent() { getDom('kotimeEnable').onclick = function () { - post(concatToken('contextPath/koTime/updateConfig'),{enable: getDom('kotimeEnable').checked},function (res) { + put(concatToken('contextPath/koTime/configs'),{enable: getDom('kotimeEnable').checked},function (res) { noticeSuccess('{{tab.configuration.kotime-config.change-ok-tip}}'); },function () { noticeError('{{tab.configuration.kotime-config.change-fail-tip}}'); @@ -161,7 +196,7 @@ }; getDom('exceptionEnable').onclick = function () { - post(concatToken('contextPath/koTime/updateConfig'),{exceptionEnable: getDom('exceptionEnable').checked},function (res) { + put(concatToken('contextPath/koTime/configs'),{exceptionEnable: getDom('exceptionEnable').checked},function (res) { noticeSuccess('{{tab.configuration.kotime-config.change-ok-tip}}') },function () { noticeError('{{tab.configuration.kotime-config.change-fail-tip}}') @@ -169,7 +204,7 @@ }; getDom('logEnable').onclick = function () { - post(concatToken('contextPath/koTime/updateConfig'),{logEnable: getDom('logEnable').checked},function (res) { + put(concatToken('contextPath/koTime/configs'),{logEnable: getDom('logEnable').checked},function (res) { noticeSuccess('{{tab.configuration.kotime-config.change-ok-tip}}') },function () { noticeError('{{tab.configuration.kotime-config.change-fail-tip}}') @@ -177,7 +212,7 @@ }; getDom('mailEnable').onclick = function () { - post(concatToken('contextPath/koTime/updateConfig'),{mailEnable: getDom('mailEnable').checked},function (res) { + put(concatToken('contextPath/koTime/configs'),{mailEnable: getDom('mailEnable').checked},function (res) { noticeSuccess('{{tab.configuration.kotime-config.change-ok-tip}}') },function () { noticeError('{{tab.configuration.kotime-config.change-fail-tip}}') @@ -185,7 +220,7 @@ }; getDom('abbreviationEnable').onclick = function () { - post(concatToken('contextPath/koTime/updateConfig'),{abbreviationEnable: getDom('abbreviationEnable').checked},function (res) { + put(concatToken('contextPath/koTime/configs'),{abbreviationEnable: getDom('abbreviationEnable').checked},function (res) { noticeSuccess('{{tab.configuration.kotime-config.change-ok-tip}}') },function () { noticeError('{{tab.configuration.kotime-config.change-fail-tip}}') @@ -194,7 +229,7 @@ getDom('languageSwitch').onclick = function () { let selectedObj = getDom('languageSwitch'); - post(concatToken('contextPath/koTime/updateConfig'),{language: selectedObj.options[selectedObj.selectedIndex].value},function (res) { + put(concatToken('contextPath/koTime/configs'),{language: selectedObj.options[selectedObj.selectedIndex].value},function (res) { noticeSuccess('{{tab.configuration.kotime-config.change-ok-tip}}') },function () { noticeError('{{tab.configuration.kotime-config.change-fail-tip}}') @@ -202,7 +237,7 @@ }; getDom('timeThresholdYes').onclick = function () { - post(concatToken('contextPath/koTime/updateConfig'),{threshold: getDom('timeThreshold').value},function (res) { + put(concatToken('contextPath/koTime/configs'),{threshold: getDom('timeThreshold').value},function (res) { noticeSuccess('{{tab.configuration.kotime-config.change-ok-tip}}') },function () { noticeError('{{tab.configuration.kotime-config.change-fail-tip}}') @@ -220,7 +255,12 @@ } function loadConfig() { - get(concatToken('contextPath/koTime/getConfig'), function (data) { + get(concatToken('contextPath/koTime/configs'), function (res) { + if (res['state']==0) { + noticeError("载入configs失败!"); + return; + } + let data = res['content']; let versionNoticeEnable = data['versionNotice']; if (versionNoticeEnable) { loadLatestVersion(); @@ -274,7 +314,12 @@ graph.removeNode(clickNodeId + "ana") methodParamMap.delete(clickNodeId + "ana") } else { - get(concatToken('contextPath/koTime/getParamGraph' + "?methodId=" + clickNodeId.replace('node', '')), function (data) { + get(concatToken('contextPath/koTime/interfaces/'+clickNodeId.replace('node', '')+'/paramMetric'), function (res) { + if (res['state']==0) { + noticeError("载入paramMetric失败!"); + return; + } + let data = res['content']; let datas = [] for (let key in data) { datas.push({"name": key + ":{{tab.interface.interface-list.show-metric.avg}} " + data[key]['avgRunTime'] + " ms"}) @@ -334,7 +379,7 @@ }; - function showMethods(name) { + function showMethods(methodId) { let exceptionDetailDom = getDom('layerDemo'); exceptionDetailDom.innerHTML = ""; let options = { @@ -349,9 +394,14 @@ UIkit.notification.closeAll(); UIkit.modal(getDom("modal-method")).show(); - get(concatToken('contextPath/koTime/getTree?methodName=' + name), function (data) { + get(concatToken('contextPath/koTime/interfaces/'+methodId+'/tree'), function (res) { + if (res['state']==0) { + noticeError("载入tree失败!"); + return; + } + let data = res['content']; let rootX = 100; - let rootY = $(window).get(0).innerHeight / 2 - 50; + let rootY = window.innerHeight / 2 - 50; data['x'] = rootX; data['y'] = rootY; graph.createNodes(data, formData); @@ -361,7 +411,12 @@ }; function showExceptions(id, message) { - get(concatToken('contextPath/koTime/getMethodsByExceptionId?exceptionId=' + id + '&message=' + message), function (data) { + get(concatToken('contextPath/koTime/exceptions/' + id + '/details?message=' + message), function (res) { + if (res['state']==0) { + noticeError("载入tree失败!"); + return; + } + let data = res['content']; let html = ''; for (let i = 0; i < data.length; i++) { html += @@ -394,7 +449,7 @@ post("contextPath/koTime/login",{'userName': userId, 'password': password},function (re) { if (re['state'] == 1) { noticeSuccess('登录成功') - sessionStorage.setItem("kotimeToken", re["token"]); + sessionStorage.setItem("kotimeToken", re['content']["token"]); location.reload(); } else { noticeError('用户名或密码错误') @@ -406,7 +461,12 @@ function searchTip(e) { let question = $('#searchText').val() - get(concatToken('contextPath/koTime/getApiTips?question=' + question), function (data) { + get(concatToken('contextPath/koTime/interfaces/searchCondidate?question=' + question), function (res) { + if (res['state']==0) { + noticeError("载入interfaces/searchCondidate失败!"); + return; + } + let data = res['content']; $("#condidates").html("") for (let i = 0; i < data.length; i++) { let name = data[i]; @@ -436,8 +496,7 @@ return; } formData.append('classFile', file); - formData.append('className', className); - postFormData(concatToken('contextPath/koTime/updateClass'),formData,function (res) { + putFormData(concatToken('contextPath/koTime/classes/'+className+'/replace'),formData,function (res) { if (res['state'] == 1) { noticeSuccess('{{tab.hotupdate.change-ok-tip}}') } else { @@ -455,7 +514,7 @@ let propertiesDom = document.querySelector("#dynamicText"); let propertiesText = propertiesDom.value; if (propertiesText && propertiesText.indexOf("=") > 0) { - post(concatToken('contextPath/koTime/updateDynamicProperties'),{text: propertiesText},function (re) { + put(concatToken('contextPath/koTime/dynamicProperties'),{text: propertiesText.trim()},function (re) { noticeSuccess('{{tab.configuration.dynamic-config.change-ok-tip}}') },function (re) { noticeError('{{tab.configuration.dynamic-config.change-fail-tip}}') @@ -467,8 +526,8 @@ } function loadDynamicProperties() { - get(concatToken('contextPath/koTime/getDynamicProperties'), function (data) { - let text = data['data']; + get(concatToken('contextPath/koTime/dynamicProperties'), function (res) { + let text = res['content']; document.querySelector("#dynamicText").value = text; }); } @@ -484,7 +543,12 @@ } function loadCpuInfo() { - get(concatToken('contextPath/koTime/getCpuInfo'), function (data) { + get(concatToken('contextPath/koTime/cpus/usage'), function (res) { + if (res['state']==0) { + noticeError("载入cpus/usage失败!"); + return; + } + let data = res['content']; let systemLoad = data['systemLoad'] * 100; let userRate = data['userRate'] * 100; let sysRate = data['sysRate'] * 100; @@ -501,12 +565,16 @@ document.querySelector("#cpuSysRate").innerHTML = `${sysRate.toFixed(2)}%`; document.querySelector("#cpuUserRate").innerHTML = `${userRate.toFixed(2)}%`; document.querySelector("#cpuLogicalAmount").innerHTML = `{{tab.summary.sysusage-metric.cpu-usage}}(${logicalNum}):`; - document.querySelector("#cpuWaitRate").innerHTML = `${waitRate.toFixed(2)}%`; }); } function loadHeapMemoryInfo() { - get(concatToken('contextPath/koTime/getHeapMemoryInfo'), function (data) { + get(concatToken('contextPath/koTime/memories/heap'), function (res) { + if (res['state']==0) { + noticeError("载入memories/heap失败!"); + return; + } + let data = res['content']; // let initValue = data['initValue'] / 1024 / 1024; // let maxValue = data['maxValue'] / 1024 / 1024; // let usedValue = data['usedValue'] / 1024 / 1024; @@ -528,8 +596,42 @@ }); } + function loadNonHeapMemoryInfo() { + get(concatToken('contextPath/koTime/memories/nonHeap'), function (res) { + if (res['state']==0) { + noticeError("载入memories/nonHeap失败!"); + return; + } + let data = res['content']; + let initValue = getSize(data['initValue']); + let maxValue = getSize(data['maxValue']); + let usedValue = getSize(data['usedValue']); + let usedRate = data['usedRate'] * 100; + var nonheapUsedRateDom = document.querySelector("#nonHeapUsedRate"); + if (usedRate > 50) { + nonheapUsedRateDom.style.color = '#cc0c0c'; + } else { + nonheapUsedRateDom.style.color = '#29da93'; + } + ; + document.querySelector("#nonHeapInit").innerHTML = initValue; + document.querySelector("#nonHeapMax").innerHTML = maxValue; + document.querySelector("#nonHeapUsed").innerHTML = usedValue; + if (data['maxValue']==-1) { + nonheapUsedRateDom.innerHTML = `-%`; + }else { + nonheapUsedRateDom.innerHTML = `${usedRate.toFixed(2)}%`; + } + }); + } + function loadPhysicalMemoryInfo() { - get(concatToken('contextPath/koTime/getPhysicalMemoryInfo'), function (data) { + get(concatToken('contextPath/koTime/memories/physical'), function (res) { + if (res['state']==0) { + noticeError("载入memories/physical失败!"); + return; + } + let data = res['content']; // let initValue = data['initValue'] / 1024 / 1024; // let thisUsedValue = data['thisUsedValue'] / 1024 / 1024; // let usedValue = data['usedValue'] / 1024 / 1024; @@ -553,6 +655,171 @@ }); } + function loadEdenSpace() { + get(concatToken('contextPath/koTime/jvmSpaces/edenSpace'), function (res) { + if (res['state']==0) { + noticeError("载jvmSpaces/edenSpace失败!"); + return; + } + let data = res['content']; + let initValue = getSize(data['initValue']); + let maxValue = getSize(data['maxValue']); + let usedValue = getSize(data['usedValue']); + let usedRate = data['usedRate'] * 100; + let edenSpaceUsedRateDom = document.querySelector("#edenSpaceUsedRate"); + if (usedRate > 50) { + edenSpaceUsedRateDom.style.color = '#cc0c0c'; + } else { + edenSpaceUsedRateDom.style.color = '#29da93'; + } + ; + document.querySelector("#edenSpaceInit").innerHTML = initValue; + document.querySelector("#edenSpaceMax").innerHTML = maxValue; + document.querySelector("#edenSpaceUsed").innerHTML = usedValue; + edenSpaceUsedRateDom.innerHTML = `${usedRate.toFixed(2)}%`; + }); + } + + function loadSurvivorSpace() { + get(concatToken('contextPath/koTime/jvmSpaces/survivorSpace'), function (res) { + if (res['state']==0) { + noticeError("载jvmSpaces/survivorSpace失败!"); + return; + } + let data = res['content']; + let initValue = getSize(data['initValue']); + let maxValue = getSize(data['maxValue']); + let usedValue = getSize(data['usedValue']); + let usedRate = data['usedRate'] * 100; + let survivorSpaceUsedRateDom = document.querySelector("#survivorSpaceUsedRate"); + if (usedRate > 50) { + survivorSpaceUsedRateDom.style.color = '#cc0c0c'; + } else { + survivorSpaceUsedRateDom.style.color = '#29da93'; + } + ; + document.querySelector("#survivorSpaceInit").innerHTML = initValue; + document.querySelector("#survivorSpaceMax").innerHTML = maxValue; + document.querySelector("#survivorSpaceUsed").innerHTML = usedValue; + survivorSpaceUsedRateDom.innerHTML = `${usedRate.toFixed(2)}%`; + }); + } + + function loadOldGenSpace() { + get(concatToken('contextPath/koTime/jvmSpaces/oldGen'), function (res) { + if (res['state']==0) { + noticeError("载jvmSpaces/oldGen失败!"); + return; + } + let data = res['content']; + let initValue = getSize(data['initValue']); + let maxValue = getSize(data['maxValue']); + let usedValue = getSize(data['usedValue']); + let usedRate = data['usedRate'] * 100; + let oldGenUsedRateDom = document.querySelector("#oldGenUsedRate"); + if (usedRate > 50) { + oldGenUsedRateDom.style.color = '#cc0c0c'; + } else { + oldGenUsedRateDom.style.color = '#29da93'; + } + ; + document.querySelector("#oldGenInit").innerHTML = initValue; + document.querySelector("#oldGenMax").innerHTML = maxValue; + document.querySelector("#oldGenUsed").innerHTML = usedValue; + oldGenUsedRateDom.innerHTML = `${usedRate.toFixed(2)}%`; + }); + } + + function loadMetaspace() { + get(concatToken('contextPath/koTime/jvmSpaces/metaspace'), function (res) { + if (res['state']==0) { + noticeError("载jvmSpaces/metaspace失败!"); + return; + } + let data = res['content']; + let initValue = getSize(data['initValue']); + let maxValue = getSize(data['maxValue']); + let usedValue = getSize(data['usedValue']); + let usedRate = data['usedRate'] * 100; + let metaspaceUsedRateDom = document.querySelector("#metaspaceUsedRate"); + if (usedRate > 50) { + metaspaceUsedRateDom.style.color = '#cc0c0c'; + } else { + metaspaceUsedRateDom.style.color = '#29da93'; + } + ; + document.querySelector("#metaspaceInit").innerHTML = initValue; + document.querySelector("#metaspaceMax").innerHTML = maxValue; + document.querySelector("#metaspaceUsed").innerHTML = usedValue; + if (data['maxValue']==-1) { + metaspaceUsedRateDom.innerHTML = `-%`; + }else { + metaspaceUsedRateDom.innerHTML = `${usedRate.toFixed(2)}%`; + } + }); + } + + function loadGcUsage() { + get(concatToken('contextPath/koTime/gcs/usage'), function (res) { + if (res['state']==0) { + noticeError("载gcs/usage失败!"); + return; + } + let data = res['content']; + let totalNum = data['totalNum']; + let fullNum = data['fullNum']; + let minorNum = data['minorNum']; + + document.querySelector("#gcTotalNum").innerHTML = totalNum; + document.querySelector("#gcMinorNum").innerHTML = minorNum; + document.querySelector("#gcFullNum").innerHTML = fullNum; + + }); + } + + function loadThreadUsage() { + get(concatToken('contextPath/koTime/threads/usage'), function (res) { + if (res['state']==0) { + noticeError("载threads/usage失败!"); + return; + } + let data = res['content']; + let totalNum = data['totalNum']; + let runnableNum = data['runnableNum']; + let deadlockNum = data['deadLockNum']; + + let threadLockNumDom = document.querySelector("#threadLockNum"); + if (deadlockNum > 0) { + threadLockNumDom.style.color = '#cc0c0c'; + } else { + threadLockNumDom.style.color = '#29da93'; + } + + document.querySelector("#threadTotalNum").innerHTML = totalNum; + document.querySelector("#threadRunnableNum").innerHTML = runnableNum; + threadLockNumDom.innerHTML = deadlockNum; + + }); + } + + function loadClassUsage() { + get(concatToken('contextPath/koTime/classes/usage'), function (res) { + if (res['state']==0) { + noticeError("载classes/usage失败!"); + return; + } + let data = res['content']; + let totalNum = data['totalClassNum']; + let currentClassNum = data['currentClassNum']; + let unloadedClassNum = data['unloadedClassNum']; + + document.querySelector("#classTotalNum").innerHTML = totalNum; + document.querySelector("#classCurrentNum").innerHTML = currentClassNum; + document.querySelector("#classDeleteNum").innerHTML = unloadedClassNum; + + }); + } + const cpuUsageColorMap = { 'RED': '#cc0c0c', 'YELLOW': '#f0ad4e', @@ -573,7 +840,12 @@ function loadThreadsInfo(queryState) { queryState = queryState || ''; - get(concatToken('contextPath/koTime/getThreadsInfo?state=' + queryState), function (data) { + get(concatToken('contextPath/koTime/threads?state=' + queryState), function (res) { + if (res['state']==0) { + noticeError("载入threads失败!"); + return; + } + let data = res['content']; let statistics = data['statistics']; let all = statistics['all']; let RUNNABLE = statistics['RUNNABLE'] || 0; @@ -592,9 +864,9 @@ let threads = data['threads']; let colors = { 'RUNNABLE': '#32d296', - 'BLOCKED': '#cc0c0c', - 'WAITING': '#ad7070', - 'TIMED_WAITING': '#ad7070' + 'BLOCKED': '#e0ce86', + 'WAITING': '#eec793', + 'TIMED_WAITING': '#d99292' } for (let i = 0; i < threads.length; i++) { let thread = threads[i]; @@ -604,9 +876,14 @@ let state = thread['state']; let stacks = thread['stacks']; let cpuUsage = thread['cpuUsage']; + let deadLock = thread['deadLock']; let cpuUsageColor = getCpuUsageColor(cpuUsage); threadMap[id + ''] = stacks; - html += `
  • id=${id}   name=${name}   class=${classType}   {{tab.thread.thread-list.cpu-usage}}:${cpuUsage}%   ${state}
  • `; + if (deadLock) { + html += `
  • id=${id}   name=${name}   class=${classType}   {{tab.thread.thread-list.cpu-usage}}:${cpuUsage}%   ${state}   DEADLOCK
  • `; + }else { + html += `
  • id=${id}   name=${name}   class=${classType}   {{tab.thread.thread-list.cpu-usage}}:${cpuUsage}%   ${state}
  • `; + } } element.innerHTML = html; }); @@ -638,7 +915,16 @@ let searchText = searchDom.value; let apiSortName = getDom('apiSortName') let apiSortType = getDom('apiSortType') - let url = concatToken(`contextPath/koTime/exportApis?question=${searchText}&orderBy=${apiSortName.value}&sort=${apiSortType.value}`); + let url = concatToken(`contextPath/koTime/interfaces/export?question=${searchText}&orderBy=${apiSortName.value}&sort=${apiSortType.value}`); + let a = document.createElement("a"); + a.style.display='none'; + a.href = url; + a.click(); + setTimeout(()=>{a.remove();},3000); + } + + function dumpHeap() { + let url = concatToken(`contextPath/koTime/memories/heap/export`); let a = document.createElement("a"); a.style.display='none'; a.href = url; @@ -682,116 +968,173 @@
      -
    • +
    • -
      +
      -
      - {{tab.summary.interface-metric.total-num}}
      - 0 +
      + {{tab.summary.interface-metric.total-num}}:0 +
      +
        +
      • {{tab.summary.interface-metric.delay-num}}:0
      • +
      • {{tab.summary.interface-metric.normal-num}}:0
      • +
      -
      - {{tab.summary.interface-metric.delay-num}} -
      - 0 +
      + {{tab.summary.response-metric.avg-num}}:0 +
      +
        +
      • {{tab.summary.response-metric.max-num}}:0
      • +
      • {{tab.summary.response-metric.min-num}}:0
      • +
      -
      - {{tab.summary.interface-metric.normal-num}} -
      - 0
      +
      + {{tab.summary.interface-metric.avg-call-num}}:0 +
      +
        +
      • {{tab.summary.interface-metric.max-call-num}}:0
      • +
      • {{tab.summary.interface-metric.min-call-num}}:0
      • +
      +
      -
      - {{tab.summary.response-metric.avg-num}} -
      - 0
      +
      + {{tab.summary.sysusage-metric.physical-memory}}:0 +
      +
        +
      • {{tab.summary.sysusage-metric.physical-memory.total}}:0
      • +
      • {{tab.summary.sysusage-metric.physical-memory.used}}:0
      • +
      • {{tab.summary.sysusage-metric.physical-memory.current-used}}:0
      • +
      +
      -
      - {{tab.summary.response-metric.max-num}} -
      - 0
      +
      + > + {{tab.summary.sysusage-metric.heap-memory}}:0 +
      +
        +
      • {{tab.summary.sysusage-metric.heap-memory.init}}:0
      • +
      • {{tab.summary.sysusage-metric.heap-memory.max}}:0
      • +
      • {{tab.summary.sysusage-metric.heap-memory.used}}:0
      • +
      +
      -
      - {{tab.summary.response-metric.min-num}} -
      - 0
      +
      + {{tab.summary.sysusage-metric.non-heap-memory}}:0 +
      +
        +
      • {{tab.summary.sysusage-metric.heap-memory.init}}:0
      • +
      • {{tab.summary.sysusage-metric.heap-memory.max}}:0
      • +
      • {{tab.summary.sysusage-metric.heap-memory.used}}:0
      • +
      +
      +
      +
      + + +
      +
      +
      + {{tab.summary.sysusage-metric.eden-space}}:0 +
      +
        +
      • {{tab.summary.sysusage-metric.heap-memory.init}}:0
      • +
      • {{tab.summary.sysusage-metric.heap-memory.max}}: 0
      • +
      • {{tab.summary.sysusage-metric.heap-memory.used}}:0
      • +
      +
      +
      +
      +
      + {{tab.summary.sysusage-metric.survivor-space}}:0 +
      +
        +
      • {{tab.summary.sysusage-metric.heap-memory.init}}:0
      • +
      • {{tab.summary.sysusage-metric.heap-memory.max}}: 0
      • +
      • {{tab.summary.sysusage-metric.heap-memory.used}}:0
      • +
      +
      +
      +
      +
      + {{tab.summary.sysusage-metric.old-gen}}:0 +
      +
        +
      • {{tab.summary.sysusage-metric.heap-memory.init}}:0
      • +
      • {{tab.summary.sysusage-metric.heap-memory.max}}: 0
      • +
      • {{tab.summary.sysusage-metric.heap-memory.used}}:0
      • +
      +
      +
      +
      +
      + {{tab.summary.sysusage-metric.meta-space}}:0 +
      +
        +
      • {{tab.summary.sysusage-metric.heap-memory.init}}:0
      • +
      • {{tab.summary.sysusage-metric.heap-memory.max}}: 0
      • +
      • {{tab.summary.sysusage-metric.heap-memory.used}}:0
      • +
      +
      -
      +
      -
      - {{tab.summary.sysusage-metric.cpu-usage}}:0 +
      + {{tab.summary.sysusage-metric.cpu-usage}}:0
      -
        -
      • {{tab.summary.sysusage-metric.cpu-usage.user-usage}}:0
      • -
      • {{tab.summary.sysusage-metric.cpu-usage.sys-usage}}:0
      • -
      • {{tab.summary.sysusage-metric.cpu-usage.io-waiting}}:0
      • +
          +
        • {{tab.summary.sysusage-metric.cpu-usage.user-usage}}:0
        • +
        • {{tab.summary.sysusage-metric.cpu-usage.sys-usage}}:0
      -
      - {{tab.summary.sysusage-metric.heap-memory}}:0 +
      + {{tab.summary.sysusage-metric.gc-num}}:0
      -
        -
      • {{tab.summary.sysusage-metric.heap-memory.init}}:0
      • -
      • {{tab.summary.sysusage-metric.heap-memory.max}}:0
      • -
      • {{tab.summary.sysusage-metric.heap-memory.used}}:0
      • +
          +
        • {{tab.summary.sysusage-metric.gc-num.minor}}:0
        • +
        • {{tab.summary.sysusage-metric.gc-num.full}}:0
      -
      - {{tab.summary.sysusage-metric.physical-memory}}:0 +
      + {{tab.summary.sysusage-metric.thread-num}}:0
      -
        -
      • {{tab.summary.sysusage-metric.physical-memory.total}}:0 -
      • -
      • {{tab.summary.sysusage-metric.physical-memory.used}}:0
      • -
      • {{tab.summary.sysusage-metric.physical-memory.current-used}}:0
      • +
          +
        • {{tab.summary.sysusage-metric.thread-num.runnable}}:0
        • +
        • {{tab.summary.sysusage-metric.thread-num.deadlock}}:0
        • +
        +
      +
      +
      +
      + {{tab.summary.sysusage-metric.loaded-class-num}}:0 +
      +
        +
      • {{tab.summary.sysusage-metric.loaded-class-num.current}}:0
      • +
      • {{tab.summary.sysusage-metric.loaded-class-num.unloaded}}:0