This commit is contained in:
huoyo 2021-05-16 11:05:03 +08:00
parent 6e4e2de9c2
commit 8a424fc2cb
19 changed files with 364 additions and 33 deletions

View File

@ -63,8 +63,9 @@ http://huoyo.gitee.io/ko-time
> V1.8支持Mybatis的Mapper监测、新增最大/最小运行时间、修复小数位数过长页面边界溢出的bug
> V1.9添加异常监测开放数据接口修复与swagger冲突bug添加配置动态更新功能以及重构数据存储机制
> V1.9过度版本
> V2.0.0添加异常监测开放数据接口修复与swagger冲突bug添加配置动态更新功能以及重构数据存储机制
#### 特别说明
1.本项目使用java8开发其他版本未曾试验如有什么bug还请告知

View File

@ -1,3 +1,5 @@
* [简介](introduce)
* [快速上手](getstart)
* [UI页面说明](uiguide)
* [API数据接口](apiintro)
* [常见问题](questions)

282
docs/apiintro.md Normal file
View File

@ -0,0 +1,282 @@
## 获取接口方法列表
* 接口名
>`GET` /koTime/getApis
* 返回示例
```json
[
{
"id":"com.example.demo.controller.IndexController.test1",
"name":"IndexController.test1",
"className":"com.example.demo.controller.IndexController",
"methodName":"test1",
"value":2001.38,
"avgRunTime":2001.38,
"maxRunTime":2001.9,
"minRunTime":2001.19,
"methodType":"Controller",
"exceptionNum":0,
"children":[
],
"exceptions":[
]
},
{
"id":"com.example.demo.controller.IndexController.test2",
"name":"IndexController.test1",
"className":"com.example.demo.controller.IndexController",
"methodName":"test1",
"value":2001.38,
"avgRunTime":2001.38,
"maxRunTime":2001.9,
"minRunTime":2001.19,
"methodType":"Controller",
"exceptionNum":0,
"children":[
],
"exceptions":[
]
}
]
```
## 获取方法列表调用链路
* 接口名
>`GET` /koTime/getTree?methodName=每一个方法的id
* 返回示例
```json
{
"id":"com.example.demo.controller.IndexController.test1",
"name":"IndexController.test1",
"className":"com.example.demo.controller.IndexController",
"methodName":"test1",
"value":2001.38,
"avgRunTime":2001.38,
"maxRunTime":2001.9,
"minRunTime":2001.19,
"methodType":"Controller",
"exceptionNum":0,
"children":[
{
"id":"com.example.demo.service.impl.IndexServiceImpl.getParents",
"name":"IndexServiceImpl.getParents",
"className":"com.example.demo.service.impl.IndexServiceImpl",
"methodName":"getParents",
"value":0.47,
"avgRunTime":0.47,
"maxRunTime":0.59,
"minRunTime":0.43,
"methodType":"Service",
"exceptionNum":1,
"children":[
{
"id":"com.example.demo.service.impl.IndexServiceImpl.index2",
"name":"IndexServiceImpl.index2",
"className":"com.example.demo.service.impl.IndexServiceImpl",
"methodName":"index2",
"value":0.35,
"avgRunTime":0.35,
"maxRunTime":0.4,
"minRunTime":0.32,
"methodType":"Service",
"exceptionNum":0,
"children":[
],
"exceptions":[
]
}
],
"exceptions":[
{
"id":"java.lang.RuntimeExceptionRuntimeException获取信息失败",
"name":"RuntimeException",
"className":"java.lang.RuntimeException",
"message":"获取信息失败",
"location":91,
"methodName":null,
"occurClassName":null
}
]
},
{
"id":"com.example.demo.service.impl.IndexServiceImpl.getUsers",
"name":"IndexServiceImpl.getUsers",
"className":"com.example.demo.service.impl.IndexServiceImpl",
"methodName":"getUsers",
"value":1000.91,
"avgRunTime":1000.91,
"maxRunTime":1006.25,
"minRunTime":1000.79,
"methodType":"Service",
"exceptionNum":1,
"children":[
{
"id":"com.example.demo.service.impl.IndexServiceImpl.index1",
"name":"IndexServiceImpl.index1",
"className":"com.example.demo.service.impl.IndexServiceImpl",
"methodName":"index1",
"value":0.42,
"avgRunTime":0.42,
"maxRunTime":3.81,
"minRunTime":0.37,
"methodType":"Service",
"exceptionNum":0,
"children":[
{
"id":"com.example.demo.service.impl.IndexServiceImpl.test2",
"name":"IndexServiceImpl.test2",
"className":"com.example.demo.service.impl.IndexServiceImpl",
"methodName":"test2",
"value":0.01,
"avgRunTime":0.01,
"maxRunTime":0.02,
"minRunTime":"0.0",
"methodType":"Service",
"exceptionNum":0,
"children":[
],
"exceptions":[
]
},
{
"id":"com.example.demo.service.impl.IndexServiceImpl.test1",
"name":"IndexServiceImpl.test1",
"className":"com.example.demo.service.impl.IndexServiceImpl",
"methodName":"test1",
"value":0.04,
"avgRunTime":0.04,
"maxRunTime":0.08,
"minRunTime":0.03,
"methodType":"Service",
"exceptionNum":0,
"children":[
],
"exceptions":[
]
}
],
"exceptions":[
]
}
],
"exceptions":[
{
"id":"java.lang.RuntimeExceptionRuntimeException认证失败",
"name":"RuntimeException",
"className":"java.lang.RuntimeException",
"message":"认证失败",
"location":82,
"methodName":null,
"occurClassName":null
}
]
}
],
"exceptions":[
]
}
```
## 获取异常列表
* 接口名
>`GET` /koTime/getExceptions
* 返回示例
```json
[
{
"id":"java.lang.RuntimeExceptionRuntimeException认证失败",
"name":"RuntimeException",
"className":"java.lang.RuntimeException",
"message":"认证失败",
"value":82
},
{
"id":"java.lang.RuntimeExceptionRuntimeException获取信息失败",
"name":"RuntimeException",
"className":"java.lang.RuntimeException",
"message":"获取信息失败",
"value":91
}
]
```
## 获取异常详情
* 接口名
> `GET` /koTime/getMethodsByExceptionId?exceptionId=xx
* 返回示例
```json
[
{
"id":"java.lang.RuntimeExceptionRuntimeException获取信息失败",
"name":"RuntimeException",
"className":"java.lang.RuntimeException",
"message":"获取信息失败",
"location":91,
"methodName":"getParents",
"occurClassName":"com.example.demo.service.impl.IndexServiceImpl"
}
]
```
## 获取当前配置信息
* 接口名
>`GET` /koTime/getConfig
* 返回示例
```json
{
"logLanguage":"chinese",
"kotimeEnable":true,
"logEnable":false,
"timeThreshold":"800.0",
"exceptionEnable":true,
"dataSaver":"memory"
}
```
## 更新当前配置信息
* 接口名
>`POST` /koTime/updateConfig
* 参数示例
```json
{
"kotimeEnable":true,
"logEnable":false,
"timeThreshold":"800.0",
"exceptionEnable":true
}
```

BIN
docs/apis.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
docs/ff.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

View File

@ -7,7 +7,7 @@
<dependency>
<groupId>cn.langpy</groupId>
<artifactId>ko-time</artifactId>
<version>1.9</version>
<version>2.0.0</version>
</dependency>
<dependency>
@ -30,12 +30,12 @@
* 可选配置
>
> koTime.enable=false # 是否开启koTime默认开启当为false时关闭koTime
> koTime.enable=true # 是否开启koTime默认开启当为false时关闭koTime
> koTime.log.enable=false # 是否开启控制输出默认false
> koTime.log.language=chinese # 控制台输出语言english/chinese默认chinese
> koTime.time.threshold=800.0 # 时间阈值用于前端展示大于阈值显示红色小于阈值显示绿色默认800
> koTime.ui.template=thymeleaf # 前端页面模板默认为freemarker可选thymeleaf 与引入的pom依赖对应
> koTime.exception.enable=true # 是否开启异常检测默认为false,开启后会对方法内部抛出的异常进行统计 v1.9开始支持
> koTime.ui.template=freemarker # 前端页面模板默认为freemarker可选thymeleaf 与引入的pom依赖对应
> koTime.exception.enable=true # 是否开启异常检测默认为false,开启后会对方法内部抛出的异常进行统计 v2.0.0开始支持
## 访问

View File

@ -37,7 +37,9 @@ http://huoyo.gitee.io/ko-time/example
> V1.8支持Mybatis的Mapper监测、新增最大/最小运行时间、修复小数位数过长页面边界溢出的bug
> V1.9添加异常监测开放数据接口修复与swagger冲突bug添加配置动态更新功能以及重构数据存储机制
> V1.9:过度版本
> V2.0.0添加异常监测开放数据接口修复与swagger冲突bug添加配置动态更新功能以及重构数据存储机制
## 作者
> Huoyo/Zhang Chang

BIN
docs/pz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

46
docs/uiguide.md Normal file
View File

@ -0,0 +1,46 @@
## 首页面板
首页有六个统计指标,分别是
`总接口数``延迟响应接口数``正常响应接口数``平均响应``最大响应``最小响应`
接口是否延迟取决于`koTime.time.threshold`的配置,大于该阈值即表示延迟,显示为红色,如下图
![](zl.png)
## 接口列表
该列表展示的是监测到被请求过的接口,按照其平均响应时间倒序排列,超过`koTime.time.threshold`的接口显示红色,如下图:
![](apis.png)
点击每一个接口后会显示该接口的方法调用链路、各个方法的运行时间以及该方法是否发生了异常,方法节点可拖动
![](ff.png)
## 异常列表
异常列表以异常为切入点,并显示每个异常发生的位置
![](yc.png)
## 配置面板
配置面板有四项配置,可在不重启系统的情况下进行配置
`开启koTime监测` :该开关对应`koTime.enable`默认开启当不需要koTime时可选择关闭
`开启异常监测` :该开关对应`koTime.exception.enable`,默认关闭,当需要时可选择开启
`开启控制台日志` :该开关对应`koTime.log.enable`,默认关闭,当需要时可选择开启,即可在控制台打印日志
`方法运行时间阈值` :该开关对应`koTime.time.threshold`默认800ms可调整调整后接口列表和方法节点的颜色将以新的阈值变化
![](pz.png)

View File

@ -1,5 +0,0 @@
## 首页
访问/koTime路径后即可打开首页如下图

BIN
docs/yc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
docs/zl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -6,7 +6,7 @@
<groupId>cn.langpy</groupId>
<artifactId>ko-time</artifactId>
<version>1.9-SNAPSHOT</version>
<version>2.0.0</version>
<name>koTime</name>
<description>koTime</description>
<licenses>

View File

@ -7,10 +7,7 @@ import cn.langpy.kotime.util.Context;
import cn.langpy.kotime.util.MethodType;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toList;
@ -70,7 +67,7 @@ public class MemoryBase implements GraphService {
exceptionRelation.setId(sourceMethodNode.getId() + exceptionNode.getId());
exceptionRelation.setSourceId(sourceMethodNode.getId());
exceptionRelation.setTargetId(exceptionNode.getId());
exceptionRelation.setLocation(exceptionNode.getLocation());
exceptionRelation.setLocation(exceptionNode.getValue());
ExceptionRelation old = exceptionRelations.get(exceptionRelation.getId());
if (null == old) {
exceptionRelations.put(exceptionRelation.getId(), exceptionRelation);
@ -118,7 +115,13 @@ public class MemoryBase implements GraphService {
for (MethodNode methodNode : methodNodes.values()) {
if (MethodType.Controller == methodNode.getMethodType()) {
String id = methodNode.getId();
MethodRelation relation = methodRelations.values().stream().filter(methodRelation -> methodRelation.getTargetId().equals(id)).findFirst().get();
Optional<MethodRelation> relations = methodRelations.values().stream().filter(methodRelation -> methodRelation.getTargetId().equals(id)).findFirst();
MethodRelation relation = null;
if (relations.isPresent()) {
relation = relations.get();
}else{
continue;
}
MethodInfo methodInfo = new MethodInfo();
methodInfo.setId(methodNode.getId());
methodInfo.setName(methodNode.getName());

View File

@ -27,7 +27,7 @@ public class RunTimeHandler implements MethodInterceptor {
exception.setName(e.getClass().getSimpleName());
exception.setClassName(e.getClass().getName());
exception.setMessage(e.getMessage());
exception.setLocation(e.getStackTrace()[0].getLineNumber());
exception.setValue(e.getStackTrace()[0].getLineNumber());
exception.setId(exception.getClassName() + exception.getName() + exception.getMessage());
MethodNode current = InvokeService.getCurrentMethodNode(invocation, 0.0);
if (current.getClassName().equals(e.getStackTrace()[0].getClassName())) {

View File

@ -7,7 +7,7 @@ public class ExceptionNode {
private String name;
private String className;
private String message;
private Integer location;
private Integer value;
public String getId() {
return id;
@ -41,12 +41,12 @@ public class ExceptionNode {
this.message = message;
}
public Integer getLocation() {
return location;
public Integer getValue() {
return value;
}
public void setLocation(Integer location) {
this.location = location;
public void setValue(Integer value) {
this.value = value;
}
@Override

View File

@ -7,7 +7,7 @@ import java.util.List;
import java.util.Objects;
public class MethodInfo implements Comparable<MethodInfo> {
public class MethodInfo implements Comparable<MethodInfo> {
private String id;
private String name;
private String className;

View File

@ -22,13 +22,13 @@ function MethodGraph(canvas) {
}
let $info = "\n" +
"<ul id=\"" + name + "\" class='node' style=\"z-index:1;position: absolute;background-color: #636c6c;list-style: none;box-shadow: 5px 1px 5px #888888;cursor:default\">\n" +
" <li class='nodeli' style=\"position:relative;background-color: "+bkColor+";padding-left:10px;padding-right:10px\"><a style=\"color: white;font-size: 12px\">指标</a></li>\n" +
" <li class='nodeli' style=\"position:relative;background-color: "+bkColor+";padding-left:10px;padding-right:10px\"><a style=\"color: white;font-size: 12px;border-radius:10px 10px 0px 0px;\">指标</a></li>\n" +
" <li class='nodeli' style=\"position:relative;padding-left:10px;padding-right:10px;border-bottom: 1px solid lightslategrey;background-color: #636c6c\"><a style=\"color: white;font-size: 9px\">方法:" + data.name + "</a></li>\n" +
" <li class='nodeli' style=\"position:relative;padding-left:10px;padding-right:10px;border-bottom: 1px solid lightslategrey;background-color: #636c6c\"><a style=\"color: white;font-size: 9px\">类型:" + data.methodType + "</a></li>\n" +
" <li class='nodeli' style=\"position:relative;padding-left:10px;padding-right:10px;border-bottom: 1px solid lightslategrey;background-color: #636c6c\"><a style=\"color: white;font-size: 9px\">平均耗时:" + data.avgRunTime + " ms</a></li>\n" +
" <li class='nodeli' style=\"position:relative;padding-left:10px;padding-right:10px;border-bottom: 1px solid lightslategrey;background-color: #636c6c\"><a style=\"color: white;font-size: 9px\">最大耗时:" + data.maxRunTime + " ms</a></li>\n" +
" <li class='nodeli' style=\"position:relative;padding-left:10px;padding-right:10px;border-bottom: 1px solid lightslategrey;background-color: #636c6c\"><a style=\"color: white;font-size: 9px\">最小耗时:" + data.minRunTime + " ms</a></li>\n" +
" <li class='nodeli' style=\"position:relative;padding-left:10px;padding-right:10px;border-bottom: 1px solid lightslategrey;background-color: "+exceptionColor+"\"><a style=\"color: white;font-size: 9px\">异常数目:" + data.exceptionNum + " 个</a></li>\n" +
" <li class='nodeli' style=\"position:relative;padding-left:10px;padding-right:10px;border-bottom: 1px solid lightslategrey;background-color: "+exceptionColor+"\"><a style=\"color: white;font-size: 9px;border-radius:0px 0px 10px 10px;\">异常数目:" + data.exceptionNum + " 个</a></li>\n" +
"</ul>";
$('#layerDemo').append($info)
$("#" + name).css({
@ -147,14 +147,14 @@ function MethodGraph(canvas) {
let pmoveNode = document.getElementById(nodeKey)
let nl = nx - o.moveNodeX ;
let nt = ny - o.moveNodeY ;
if (nl>6) {
if (nl>10) {
pmoveNode.style.left = Number(pmoveNode.style.left.replace('px',''))+10 + 'px';
}else if(nl<-6){
}else if(nl<-10){
pmoveNode.style.left = Number(pmoveNode.style.left.replace('px',''))-10 + 'px';
}
if (nt>4) {
if (nt>6) {
pmoveNode.style.top = Number(pmoveNode.style.top.replace('px',''))+10 + 'px';
}else if (nt<-4){
}else if (nt<-6){
pmoveNode.style.top = Number(pmoveNode.style.top.replace('px',''))-10 + 'px';
}
let childrenId = o.allNodeEnds.get(nodeKey)

View File

@ -79,8 +79,8 @@
<div class="layui-colla-item" >
<h2 class="layui-colla-title" th:id="${runtime.className}+'.'+${runtime.methodName}">
<span th:text="${runtime.className}+'#'+${runtime.methodName}+'&nbsp'"></span>
<span th:if="${runtime.avgRunTime ge config.timeThreshold}" class="'layui-badge layui-bg-red" th:text="'平均响应 '+${runtime.avgRunTime}+' 毫秒'"></span>
<span th:if="${runtime.avgRunTime lt config.timeThreshold}" class="'layui-badge layui-bg-green" th:text="'平均响应 '+${runtime.avgRunTime}+' 毫秒'"></span>
<span style="font-size: 12px;" th:if="${runtime.avgRunTime ge config.timeThreshold}" class="'layui-badge layui-bg-red" th:text="'平均响应 '+${runtime.avgRunTime}+' 毫秒'"></span>
<span style="font-size: 12px;" th:if="${runtime.avgRunTime lt config.timeThreshold}" class="'layui-badge layui-bg-green" th:text="'平均响应 '+${runtime.avgRunTime}+' 毫秒'"></span>
</h2>
</div>
</div>