Skip to content

Commit fd249db

Browse files
committed
optimize thread statistics
1 parent 9fe09cc commit fd249db

File tree

8 files changed

+291
-31
lines changed

8 files changed

+291
-31
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</div>
1010

1111
<div align="center">
12-
<img src='https://shields.io/badge/version-2.3.7-green.svg'>
12+
<img src='https://shields.io/badge/version-2.3.9-green.svg'>
1313
<img src='https://shields.io/badge/author-Chang Zhang-dbab09.svg'>
1414
<img src='https://shields.io/badge/dependencies-Spring|aspectjweaver|tomcat|UIKit|Metricflow-r.svg'>
1515
</div>
@@ -22,6 +22,7 @@
2222
- ✅ Find exceptions occurred in methods
2323
- ✅ Email you after finding an overtime method
2424
- ✅ Hot update online:you needn't restart it
25+
- ✅ Thread manage:show threads information
2526
- ✅ Easy to use:you needn't additional learning costs
2627
- ✅ Enough to add a pom dependency:you needn't additional deployment costs
2728

@@ -35,6 +36,7 @@
3536
- ✅ 追踪系统异常,精确定位到方法
3637
- ✅ 接口超时邮件通知,无需实时查看
3738
- ✅ 线上热更新:无需重启更新代码
39+
- ✅ 线程管理:线程实时统计与状态查看
3840
- ✅ 使用简单,无技术学习成本
3941
- ✅ pom依赖即可,无代码侵入,无多余部署成本
4042

@@ -87,6 +89,13 @@ v2.2.5开始加入了邮件通知功能,当方法耗时超过阈值之后,
8789

8890
![输入图片说明](docs/v200/image.png)
8991

92+
5.线程管理
93+
94+
v2.3.9开始加入了线程管理功能,可以统计线程状态和查看线程堆栈信息
95+
96+
![输入图片说明](docs/v220/xcgl.png)
97+
![输入图片说明](docs/v220/xcgl2.png)
98+
9099
## 重要版本说明
91100

92101
> V1.0:基本功能

docs/v220/xcgl.png

137 KB
Loading

docs/v220/xcgl2.png

86 KB
Loading

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>cn.langpy</groupId>
88
<artifactId>ko-time</artifactId>
9-
<version>2.3.8</version>
9+
<version>2.3.9</version>
1010
<name>KoTime</name>
1111
<description>A springboot tool for tracking the paths of the methods,which can help you find method's performances easily.</description>
1212
<licenses>

src/main/java/cn/langpy/kotime/controller/KoTimeController.java

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,9 @@
2121
import javax.servlet.http.HttpServletRequest;
2222
import javax.servlet.http.HttpServletResponse;
2323
import java.io.*;
24-
import java.util.Collections;
25-
import java.util.HashMap;
26-
import java.util.List;
27-
import java.util.Map;
24+
import java.util.*;
2825
import java.util.logging.Logger;
26+
import java.util.stream.Collectors;
2927

3028
/**
3129
* zhangchang
@@ -71,7 +69,7 @@ public Map isLogin(String kotoken) {
7169
if (StringUtils.hasText(kotoken)) {
7270
if (kotoken.equals(Context.getConfig().getStaticToken())) {
7371
checkLogin = true;
74-
}else {
72+
} else {
7573
checkLogin = KoUtil.isLogin(kotoken);
7674
}
7775
}
@@ -81,7 +79,7 @@ public Map isLogin(String kotoken) {
8179

8280

8381
@GetMapping
84-
public void index(String kotoken,String test, HttpServletResponse response, HttpServletRequest request) {
82+
public void index(String kotoken, String test, HttpServletResponse response, HttpServletRequest request) {
8583
if (!Context.getConfig().getEnable()) {
8684
return;
8785
}
@@ -121,14 +119,14 @@ public void index(String kotoken,String test, HttpServletResponse response, Http
121119
line = line.replace("UIKitJs", uiKitJsText);
122120
} else if (line.indexOf("MetricFlowJs") > -1) {
123121
line = line.replace("MetricFlowJs", metricFlowJsText);
124-
}else if (line.indexOf("jQueryJs") > -1) {
122+
} else if (line.indexOf("jQueryJs") > -1) {
125123
line = line.replace("jQueryJs", jQueryJsText);
126-
}else if (line.indexOf("uiKitIconsJs") > -1) {
124+
} else if (line.indexOf("uiKitIconsJs") > -1) {
127125
line = line.replace("uiKitIconsJs", uiKitIconsJs);
128-
}else if (line.indexOf("staticTokenVisitValue") > -1) {
129-
line = line.replace("staticTokenVisitValue", staticTokenVisit+"");
130-
}else if (line.indexOf("staticTokenValue") > -1) {
131-
line = line.replace("staticTokenValue", "'"+kotoken+"'");
126+
} else if (line.indexOf("staticTokenVisitValue") > -1) {
127+
line = line.replace("staticTokenVisitValue", staticTokenVisit + "");
128+
} else if (line.indexOf("staticTokenValue") > -1) {
129+
line = line.replace("staticTokenValue", "'" + kotoken + "'");
132130
}
133131
stringBuilder.append(line + "\n");
134132
}
@@ -139,6 +137,7 @@ public void index(String kotoken,String test, HttpServletResponse response, Http
139137
e.printStackTrace();
140138
}
141139
}
140+
142141
private String getResourceText(String fileName) {
143142
ClassPathResource classPathResource = new ClassPathResource(fileName);
144143
try (InputStream inputStream = classPathResource.getInputStream();
@@ -229,9 +228,9 @@ public MethodInfo getTree(String methodName) {
229228
@GetMapping("/getMethodsByExceptionId")
230229
@ResponseBody
231230
@Auth
232-
public List<ExceptionInfo> getMethodsByExceptionId(String exceptionId,String message) {
231+
public List<ExceptionInfo> getMethodsByExceptionId(String exceptionId, String message) {
233232
GraphService graphService = GraphService.getInstance();
234-
List<ExceptionInfo> exceptionInfos = graphService.getExceptionInfos(exceptionId,message);
233+
List<ExceptionInfo> exceptionInfos = graphService.getExceptionInfos(exceptionId, message);
235234
return exceptionInfos;
236235
}
237236

@@ -257,12 +256,13 @@ public boolean updateConfig(@RequestBody DefaultConfig config) {
257256
}
258257
return true;
259258
}
259+
260260
@PostMapping("/updateClass")
261261
@ResponseBody
262262
@Auth
263-
public Map updateClass(@RequestParam("classFile") MultipartFile classFile,String className) {
263+
public Map updateClass(@RequestParam("classFile") MultipartFile classFile, String className) {
264264
Map map = new HashMap();
265-
if (classFile==null || classFile.isEmpty()) {
265+
if (classFile == null || classFile.isEmpty()) {
266266
map.put("state", 0);
267267
map.put("message", "文件不能为空");
268268
return map;
@@ -288,15 +288,15 @@ public Map updateClass(@RequestParam("classFile") MultipartFile classFile,String
288288
map.put("message", "请确认类名是否正确");
289289
return map;
290290
}
291-
file = uploadFile(classFile.getBytes(),filename[0]);
291+
file = uploadFile(classFile.getBytes(), filename[0]);
292292
} catch (IOException e) {
293293
log.severe("Error class file!");
294294
map.put("state", 0);
295295
map.put("message", "无法解析文件");
296296
return map;
297297
}
298298
final ClassService classService = ClassService.getInstance();
299-
classService.updateClass(className,file.getAbsolutePath());
299+
classService.updateClass(className, file.getAbsolutePath());
300300
file.deleteOnExit();
301301

302302
map.put("state", 1);
@@ -305,19 +305,18 @@ public Map updateClass(@RequestParam("classFile") MultipartFile classFile,String
305305
}
306306

307307

308-
309-
private static File uploadFile(byte[] file,String fileName) throws IOException {
308+
private static File uploadFile(byte[] file, String fileName) throws IOException {
310309
FileOutputStream out = null;
311310
try {
312-
File targetFile = File.createTempFile(fileName, ".class", new File(System.getProperty("java.io.tmpdir")));
311+
File targetFile = File.createTempFile(fileName, ".class", new File(System.getProperty("java.io.tmpdir")));
313312
out = new FileOutputStream(targetFile.getAbsolutePath());
314313
out.write(file);
315314
out.flush();
316315
return targetFile;
317316
} catch (Exception e) {
318317
log.severe("" + e);
319-
}finally {
320-
if(out !=null){
318+
} finally {
319+
if (out != null) {
321320
out.flush();
322321
out.close();
323322
}
@@ -342,6 +341,7 @@ public HeapMemoryInfo getHeapMemoryInfo() {
342341
HeapMemoryInfo heapMemoryInfo = usageService.getHeapMemoryInfo();
343342
return heapMemoryInfo;
344343
}
344+
345345
@GetMapping("/getPhysicalMemoryInfo")
346346
@ResponseBody
347347
@Auth
@@ -363,9 +363,20 @@ public boolean clearData() {
363363
@GetMapping("/getThreadsInfo")
364364
@ResponseBody
365365
@Auth
366-
public List getThreadsInfo() {
366+
public Map getThreadsInfo(String state) {
367367
ThreadUsageService usageService = ThreadUsageService.newInstance();
368368
List<ThreadInfo> threads = usageService.getThreads();
369-
return threads;
369+
threads = threads.stream().sorted(Comparator.comparing(ThreadInfo::getState)).collect(Collectors.toList());
370+
371+
Map<String, Long> stateCounting = threads.stream().collect(Collectors.groupingBy(ThreadInfo::getState, Collectors.counting()));
372+
stateCounting.put("all",(long)threads.size());
373+
374+
Map map = new HashMap();
375+
map.put("statistics", stateCounting);
376+
if (StringUtils.hasText(state)) {
377+
threads = threads.stream().filter(a -> a.getState().equals(state)).collect(Collectors.toList());
378+
}
379+
map.put("threads", threads);
380+
return map;
370381
}
371382
}

src/main/java/cn/langpy/kotime/service/ThreadUsageService.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,9 @@ public List<ThreadInfo> getThreads() {
3737
}
3838
return list;
3939
}
40+
41+
public List<ThreadInfo> getThreads(String state) {
42+
List<ThreadInfo> threads = getThreads();
43+
return threads.stream().filter(a -> a.getState().equals(state)).collect(Collectors.toList());
44+
}
4045
}

src/main/resources/kotime-en.html

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,9 +505,68 @@
505505
loadCpuInfo();
506506
loadHeapMemoryInfo();
507507
loadPhysicalMemoryInfo();
508+
loadThreadsInfo();
508509
}
509510
});
510511
}
512+
let threadMap = new Map();
513+
function loadThreadsInfo(queryState) {
514+
queryState = queryState || '';
515+
$.get(concatToken('contextPath/koTime/getThreadsInfo?state='+queryState), function (data) {
516+
let statistics = data['statistics'];
517+
let all = statistics['all'];
518+
let RUNNABLE = statistics['RUNNABLE'] || 0;
519+
let BLOCKED = statistics['BLOCKED'] || 0;
520+
let WAITING = statistics['WAITING'] || 0;
521+
let TIMED_WAITING = statistics['TIMED_WAITING'] || 0;
522+
523+
document.querySelector("#threadNum").innerHTML = all;
524+
document.querySelector("#runnableNum").innerHTML = RUNNABLE;
525+
document.querySelector("#blockedNum").innerHTML = BLOCKED;
526+
document.querySelector("#waitingNum").innerHTML = WAITING;
527+
document.querySelector("#timedWaitingNum").innerHTML = TIMED_WAITING;
528+
529+
let element = document.getElementById('threadList');
530+
let html = '';
531+
let threads = data['threads'];
532+
let colors = {
533+
'RUNNABLE':'#32d296',
534+
'BLOCKED':'#cc0c0c',
535+
'WAITING':'#ad7070',
536+
'TIMED_WAITING':'#ad7070'
537+
}
538+
for (let i = 0; i < threads.length; i++) {
539+
let thread = threads[i];
540+
let id = thread['id'];
541+
let name = thread['name'];
542+
let classType = thread['classType'];
543+
let state = thread['state'];
544+
let stacks = thread['stacks'];
545+
threadMap[id+''] = stacks;
546+
html+=`<li onclick="showThreadInfo('${id}')" style='color: #333;font-weight: 400;font-size: 14px;'>id=<span style="font-size: 16px;font-weight: 500;">${id}</span>&nbsp; &nbsp;name=${name}&nbsp; &nbsp;class=${classType}&nbsp; &nbsp;<span style='font-size: 10px;background-color: ${colors[state]};' class="uk-label uk-label-success">${state}</span></li>`;
547+
}
548+
element.innerHTML = html;
549+
});
550+
}
551+
552+
function showThreadInfo(id) {
553+
let stacks = threadMap[id];
554+
let html = '';
555+
for (let i = 0; i < stacks.length; i++) {
556+
let stack = stacks[i];
557+
let className = stack['className']
558+
let methodName = stack['methodName']
559+
let fileName = stack['fileName']
560+
let lineNumber = stack['lineNumber']
561+
html+=`<li style='color: #333;font-weight: 400;font-size: 14px;'>${className}.${methodName}&nbsp; &nbsp;<span style='font-size: 10px;background-color: darkslategray;text-transform: unset' class="uk-label uk-label-success">${fileName}${lineNumber}</span></li>`;
562+
563+
}
564+
let threadDetailDom = document.getElementById('thread-detail');
565+
threadDetailDom.innerHTML = html;
566+
UIkit.notification.closeAll();
567+
UIkit.modal(document.getElementById("modal-thread")).show();
568+
}
569+
511570
$(document).ready(function () {
512571
refreshData();
513572
});
@@ -529,6 +588,7 @@
529588
<li id="zl" class="uk-active"><a href="#" style="color: #edeef1;font-size: 14px;text-transform: capitalize">Summary</a></li>
530589
<li id="jklb"><a href="#" style="color: #edeef1;font-size: 14px;text-transform: capitalize">Interfaces</a></li>
531590
<li><a href="#" style="color: #edeef1;font-size: 14px;text-transform: capitalize">Exceptions</a></li>
591+
<li><a href="#" style="color: #edeef1;font-size: 14px;text-transform: capitalize">Threads</a></li>
532592
<li><a href="#" style="color: #edeef1;font-size: 14px;text-transform: capitalize">Hot update</a></li>
533593
<li><a href="#" style="color: #edeef1;font-size: 14px;text-transform: capitalize">Configurations</a></li>
534594
<li><a href="#" style="color: #edeef1;font-size: 14px;text-transform: capitalize">Contact me</a><span title="Latest release" style="position: absolute;top:-10px;left: 90%;border-radius: 5px;text-transform: unset" class="uk-label" id="version-notice" onclick="window.location.href='http://www.kotime.cn/docs/kaiyuan#/v220/introduce'"></span></li>
@@ -648,6 +708,50 @@
648708
<li>exception 1 1&nbsp<span class="uk-label uk-label-danger">closed</span></li>
649709
</ul>
650710
</li>
711+
<li style="margin-left: 30%;margin-right: 30%;">
712+
<ul class="uk-flex-left" uk-tab>
713+
<li class="uk-active"><a href="#" style="text-transform: capitalize">Number of thread</a></li>
714+
</ul>
715+
<div style="margin-top: 20px;" class="uk-grid-small uk-child-width-expand@s uk-text-center" uk-grid>
716+
<div>
717+
<div onclick="loadThreadsInfo('')" id="threadNum-div" style="cursor:pointer;border-radius: 10px;background-color: #fefffe;padding: 20px" class="uk-card uk-card-default uk-card-body uk-label-success">
718+
<span style="font-size: 10px;color: #3b3f4f">ALL</span><br>
719+
<span style="font-size: 30px;color: #020718;font-weight: bold" id="threadNum" >0</span>
720+
</div>
721+
</div>
722+
<div>
723+
<div onclick="loadThreadsInfo('RUNNABLE')" id="runnableNum-div" style="cursor:pointer;border-radius: 10px;background-color: #fefffe;padding: 20px" class="uk-card uk-card-default uk-card-body uk-label-success">
724+
<span style="font-size: 10px;color: #3b3f4f">RUNNABLE</span>
725+
<br>
726+
<span style="font-size: 30px;color: #29da93;font-weight: bold" id="runnableNum">0</span>
727+
</div>
728+
</div>
729+
<div>
730+
<div onclick="loadThreadsInfo('BLOCKED')" id="blockedNum-div" style="cursor:pointer;border-radius: 10px;background-color: #fefffe;padding: 20px" class="uk-card uk-card-default uk-card-body uk-label-success">
731+
<span style="font-size: 10px;color: #3b3f4f">BLOCKED</span>
732+
<br>
733+
<span style="font-size: 30px;color: #cc0c0c;font-weight: bold" id="blockedNum">0</span></div>
734+
</div>
735+
<div>
736+
<div onclick="loadThreadsInfo('WAITING')" id="waitingNum-div" style="cursor:pointer;border-radius: 10px;background-color: #fefffe;padding: 20px" class="uk-card uk-card-default uk-card-body uk-label-success">
737+
<span style="font-size: 10px;color: #3b3f4f">WAITING</span>
738+
<br>
739+
<span style="font-size: 30px;color: #ad7070;font-weight: bold" id="waitingNum">0</span></div>
740+
</div>
741+
<div>
742+
<div onclick="loadThreadsInfo('TIMED_WAITING')" id="timedWaitingNum-div" style="cursor:pointer;border-radius: 10px;background-color: #fefffe;padding: 20px" class="uk-card uk-card-default uk-card-body uk-label-success">
743+
<span style="font-size: 10px;color: #3b3f4f">TIMED_WAITING</span>
744+
<br>
745+
<span style="font-size: 30px;color: #ad7070;font-weight: bold" id="timedWaitingNum">0</span></div>
746+
</div>
747+
</div>
748+
<ul class="uk-flex-left" uk-tab>
749+
<li class="uk-active"><a href="#" style="text-transform: capitalize">Threads</a></li>
750+
</ul>
751+
<ul id="threadList" style="background-color: rgba(245,242,242,0.96);padding: 10px;overflow-y: auto;max-height: 70%" class="uk-list uk-list-divider">
752+
<li>thread 1 1&nbsp<span class="uk-label uk-label-success">0</span></li>
753+
</ul>
754+
</li>
651755
<li style="margin-left: 35%;margin-right: 35%;">
652756
<div class="uk-card uk-card-default uk-card-body">
653757
<div id="classForm" >
@@ -709,7 +813,19 @@
709813
<a href="http://www.kotime.cn/person?version=v2.3.7">Local plugin</a></div>
710814
</li>
711815
</ul>
816+
<div id="modal-thread" uk-modal>
817+
<div class="uk-modal-dialog" style="width: 75%">
818+
<button class="uk-modal-close-default" type="button" uk-close></button>
819+
<div class="uk-modal-body uk-margin-auto-vertical" uk-overflow-auto>
820+
<ul id="thread-detail" style="background-color: rgba(245,242,242,0.96);padding: 10px;overflow-y: auto;max-height: 70%" class="uk-list uk-list-divider">
821+
</ul>
822+
</div>
823+
<div class="uk-modal-footer uk-text-right">
824+
<button class="uk-button uk-button-primary uk-modal-close" type="button">OK</button>
825+
</div>
712826

827+
</div>
828+
</div>
713829
<div id="modal-exception" uk-modal>
714830
<div class="uk-modal-dialog">
715831
<button class="uk-modal-close-default" type="button" uk-close></button>

0 commit comments

Comments
 (0)