mirror of
https://gitee.com/huoyo/ko-time.git
synced 2025-12-06 16:58:26 +08:00
add a stack to optimize the code about how to get parent method
This commit is contained in:
parent
df45e8c4dd
commit
218e4908c8
2
pom.xml
2
pom.xml
@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>cn.langpy</groupId>
|
||||
<artifactId>ko-time</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<version>2.0.1-SNAPSHOT</version>
|
||||
<name>koTime</name>
|
||||
<description>koTime</description>
|
||||
<licenses>
|
||||
|
||||
@ -1,64 +1,30 @@
|
||||
package cn.langpy.kotime.config;
|
||||
|
||||
|
||||
import cn.langpy.kotime.handler.RunTimeHandler;
|
||||
import cn.langpy.kotime.model.KoTimeConfig;
|
||||
import cn.langpy.kotime.util.Context;
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Configuration
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "ko-time")
|
||||
public class DefaultConfig {
|
||||
@Value("${koTime.enable:true}")
|
||||
private Boolean kotimeEnable;
|
||||
@Value("${koTime.log.language:chinese}")
|
||||
private Boolean enable;
|
||||
private String logLanguage;
|
||||
@Value("${koTime.log.enable:false}")
|
||||
private Boolean logEnable;
|
||||
@Value("${koTime.time.threshold:800.0}")
|
||||
private Double timeThreshold;
|
||||
@Value("${koTime.pointcut:execution(* cn.langpy.kotime.controller.KoTimeController.*(..))}")
|
||||
private Double threshold;
|
||||
private String pointcut;
|
||||
@Value("${koTime.exception.enable:false}")
|
||||
private Boolean exceptionEnable;
|
||||
@Value("${koTime.save.saver:memory}")
|
||||
private String saveSaver;
|
||||
@Value("${koTime.save.async:false}")
|
||||
private Boolean saveAsync;
|
||||
@Value("${koTime.save.thread-num:4}")
|
||||
private Integer threadNum;
|
||||
private String uiTemplate;
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void function() {
|
||||
KoTimeConfig config = new KoTimeConfig();
|
||||
config.setLogEnable(logEnable);
|
||||
config.setLogLanguage(logLanguage);
|
||||
config.setTimeThreshold(timeThreshold);
|
||||
config.setExceptionEnable(exceptionEnable);
|
||||
config.setDataSaver(saveSaver);
|
||||
config.setKotimeEnable(kotimeEnable);
|
||||
Context.setConfig(config);
|
||||
public Boolean getEnable() {
|
||||
return enable;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AspectJExpressionPointcutAdvisor configurabledvisor() {
|
||||
AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
|
||||
advisor.setExpression(pointcut);
|
||||
advisor.setAdvice(new RunTimeHandler());
|
||||
return advisor;
|
||||
}
|
||||
|
||||
public Double getTimeThreshold() {
|
||||
return timeThreshold;
|
||||
}
|
||||
|
||||
public void setTimeThreshold(Double timeThreshold) {
|
||||
this.timeThreshold = timeThreshold;
|
||||
public void setEnable(Boolean enable) {
|
||||
this.enable = enable;
|
||||
}
|
||||
|
||||
public String getLogLanguage() {
|
||||
@ -77,5 +43,59 @@ public class DefaultConfig {
|
||||
this.logEnable = logEnable;
|
||||
}
|
||||
|
||||
public Double getThreshold() {
|
||||
return threshold;
|
||||
}
|
||||
|
||||
public void setThreshold(Double threshold) {
|
||||
this.threshold = threshold;
|
||||
}
|
||||
|
||||
public String getPointcut() {
|
||||
return pointcut;
|
||||
}
|
||||
|
||||
public void setPointcut(String pointcut) {
|
||||
this.pointcut = pointcut;
|
||||
}
|
||||
|
||||
public Boolean getExceptionEnable() {
|
||||
return exceptionEnable;
|
||||
}
|
||||
|
||||
public void setExceptionEnable(Boolean exceptionEnable) {
|
||||
this.exceptionEnable = exceptionEnable;
|
||||
}
|
||||
|
||||
public String getSaveSaver() {
|
||||
return saveSaver;
|
||||
}
|
||||
|
||||
public void setSaveSaver(String saveSaver) {
|
||||
this.saveSaver = saveSaver;
|
||||
}
|
||||
|
||||
public Boolean getSaveAsync() {
|
||||
return saveAsync;
|
||||
}
|
||||
|
||||
public void setSaveAsync(Boolean saveAsync) {
|
||||
this.saveAsync = saveAsync;
|
||||
}
|
||||
|
||||
public Integer getThreadNum() {
|
||||
return threadNum;
|
||||
}
|
||||
|
||||
public void setThreadNum(Integer threadNum) {
|
||||
this.threadNum = threadNum;
|
||||
}
|
||||
|
||||
public String getUiTemplate() {
|
||||
return uiTemplate;
|
||||
}
|
||||
|
||||
public void setUiTemplate(String uiTemplate) {
|
||||
this.uiTemplate = uiTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,82 @@
|
||||
package cn.langpy.kotime.config;
|
||||
import cn.langpy.kotime.handler.RunTimeHandler;
|
||||
import cn.langpy.kotime.util.Common;
|
||||
import cn.langpy.kotime.util.Context;
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ComponentScan("cn.langpy.kotime")
|
||||
@Configuration
|
||||
public class LoadConfig {
|
||||
public static Logger log = Logger.getLogger(Common.class.toString());
|
||||
|
||||
@Value("${koTime.enable:true}")
|
||||
private Boolean kotimeEnable;
|
||||
@Value("${koTime.log.language:chinese}")
|
||||
private String logLanguage;
|
||||
@Value("${koTime.log.enable:false}")
|
||||
private Boolean logEnable;
|
||||
@Value("${koTime.time.threshold:800.0}")
|
||||
private Double timeThreshold;
|
||||
@Value("${koTime.pointcut:execution(* cn.langpy.kotime.controller.KoTimeController.*(..))}")
|
||||
private String pointcut;
|
||||
@Value("${koTime.exception.enable:false}")
|
||||
private Boolean exceptionEnable;
|
||||
@Value("${koTime.ui.template}")
|
||||
private String uiTemplate;
|
||||
@Value("${koTime.save.saver:memory}")
|
||||
private String saveSaver;
|
||||
@Value("${koTime.save.async:false}")
|
||||
private Boolean saveAsync;
|
||||
@Value("${koTime.save.thread-num:4}")
|
||||
private Integer threadNum;
|
||||
|
||||
@Resource
|
||||
private DefaultConfig defaultConfig;
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void function() {
|
||||
getUiTemplate();
|
||||
DefaultConfig config = new DefaultConfig();
|
||||
config.setLogEnable(defaultConfig.getLogEnable()==null?logEnable:defaultConfig.getLogEnable());
|
||||
config.setLogLanguage(defaultConfig.getLogLanguage()==null?logLanguage:defaultConfig.getLogLanguage());
|
||||
config.setThreshold(defaultConfig.getThreshold()==null?timeThreshold:defaultConfig.getThreshold());
|
||||
config.setExceptionEnable(defaultConfig.getExceptionEnable()==null?exceptionEnable:defaultConfig.getExceptionEnable());
|
||||
config.setSaveSaver(defaultConfig.getSaveSaver()==null?saveSaver:defaultConfig.getSaveSaver());
|
||||
config.setEnable(defaultConfig.getEnable()==null?kotimeEnable:defaultConfig.getEnable());
|
||||
config.setUiTemplate(defaultConfig.getUiTemplate()==null?(uiTemplate==null?getUiTemplate():uiTemplate):defaultConfig.getUiTemplate());
|
||||
Context.setConfig(config);
|
||||
}
|
||||
|
||||
public String getUiTemplate() {
|
||||
try {
|
||||
LoadConfig.class.getClassLoader().loadClass("freemarker.template.Configuration");
|
||||
log.info("loaded freemarker");
|
||||
return "freemarker";
|
||||
} catch (ClassNotFoundException e) {
|
||||
}
|
||||
try {
|
||||
LoadConfig.class.getClassLoader().loadClass("org.thymeleaf.Thymeleaf");
|
||||
log.info("loaded thymeleaf");
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("cannot find any ui-template,please add spring-boot-starter-freemarker(or thymeleaf) to pom.xml ");
|
||||
}
|
||||
return "thymeleaf";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AspectJExpressionPointcutAdvisor configurabledvisor() {
|
||||
AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
|
||||
advisor.setExpression(defaultConfig.getPointcut()==null?pointcut:defaultConfig.getPointcut());
|
||||
advisor.setAdvice(new RunTimeHandler());
|
||||
return advisor;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
package cn.langpy.kotime.controller;
|
||||
|
||||
import cn.langpy.kotime.config.DefaultConfig;
|
||||
import cn.langpy.kotime.model.*;
|
||||
import cn.langpy.kotime.service.GraphService;
|
||||
import cn.langpy.kotime.util.Context;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@ -14,8 +14,7 @@ import java.util.List;
|
||||
@Controller
|
||||
@RequestMapping("/koTime")
|
||||
public class KoTimeController {
|
||||
@Value("${koTime.ui.template:freemarker}")
|
||||
private String showTemplate;
|
||||
|
||||
|
||||
@GetMapping
|
||||
public String index(Model model) {
|
||||
@ -29,7 +28,7 @@ public class KoTimeController {
|
||||
model.addAttribute("system", system);
|
||||
model.addAttribute("config", Context.getConfig());
|
||||
String template = "index-freemarker";
|
||||
if ("thymeleaf".equals(showTemplate)) {
|
||||
if ("thymeleaf".equals(Context.getConfig().getUiTemplate())) {
|
||||
template = "index-thymeleaf";
|
||||
}
|
||||
return template;
|
||||
@ -37,7 +36,7 @@ public class KoTimeController {
|
||||
|
||||
@GetMapping("/getConfig")
|
||||
@ResponseBody
|
||||
public KoTimeConfig getConfig() {
|
||||
public DefaultConfig getConfig() {
|
||||
return Context.getConfig();
|
||||
}
|
||||
|
||||
@ -84,9 +83,9 @@ public class KoTimeController {
|
||||
@PostMapping("/updateConfig")
|
||||
@ResponseBody
|
||||
public boolean updateConfig(@RequestBody KoTimeConfig config) {
|
||||
KoTimeConfig koTimeConfig = Context.getConfig();
|
||||
DefaultConfig koTimeConfig = Context.getConfig();
|
||||
if (config.getKotimeEnable()!=null) {
|
||||
koTimeConfig.setKotimeEnable(config.getKotimeEnable());
|
||||
koTimeConfig.setEnable(config.getKotimeEnable());
|
||||
}
|
||||
if (config.getExceptionEnable()!=null) {
|
||||
koTimeConfig.setExceptionEnable(config.getExceptionEnable());
|
||||
@ -95,7 +94,7 @@ public class KoTimeController {
|
||||
koTimeConfig.setLogEnable(config.getLogEnable());
|
||||
}
|
||||
if (config.getTimeThreshold()!=null) {
|
||||
koTimeConfig.setTimeThreshold(config.getTimeThreshold());
|
||||
koTimeConfig.setThreshold(config.getTimeThreshold());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -31,6 +31,9 @@ public class MemoryBase implements GraphService {
|
||||
|
||||
@Override
|
||||
public void addMethodNode(MethodNode methodNode) {
|
||||
if (null==methodNode) {
|
||||
return;
|
||||
}
|
||||
if (!methodNodes.containsKey(methodNode.getId())) {
|
||||
methodNodes.put(methodNode.getId(), methodNode);
|
||||
}
|
||||
@ -200,9 +203,9 @@ public class MemoryBase implements GraphService {
|
||||
if (null == controllerApis || controllerApis.size() == 0) {
|
||||
return systemStatistic;
|
||||
}
|
||||
int delayNum = (int) controllerApis.stream().filter(controllerApi -> controllerApi.getAvgRunTime() >= Context.getConfig().getTimeThreshold()).count();
|
||||
int delayNum = (int) controllerApis.stream().filter(controllerApi -> controllerApi.getAvgRunTime() >= Context.getConfig().getThreshold()).count();
|
||||
systemStatistic.setDelayNum(delayNum);
|
||||
int normalNum = (int) controllerApis.stream().filter(controllerApi -> controllerApi.getAvgRunTime() < Context.getConfig().getTimeThreshold()).count();
|
||||
int normalNum = (int) controllerApis.stream().filter(controllerApi -> controllerApi.getAvgRunTime() < Context.getConfig().getThreshold()).count();
|
||||
systemStatistic.setNormalNum(normalNum);
|
||||
int totalNum = (int) controllerApis.stream().count();
|
||||
systemStatistic.setTotalNum(totalNum);
|
||||
|
||||
@ -5,6 +5,7 @@ import cn.langpy.kotime.service.GraphService;
|
||||
import cn.langpy.kotime.model.MethodNode;
|
||||
import cn.langpy.kotime.service.InvokeService;
|
||||
import cn.langpy.kotime.util.Context;
|
||||
import cn.langpy.kotime.util.MethodStack;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
@ -12,13 +13,15 @@ public class RunTimeHandler implements MethodInterceptor {
|
||||
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
boolean kotimeEnable = Context.getConfig().getKotimeEnable();
|
||||
boolean kotimeEnable = Context.getConfig().getEnable();
|
||||
if (!kotimeEnable) {
|
||||
return invocation.proceed();
|
||||
}
|
||||
boolean exceptionEnable = Context.getConfig().getExceptionEnable();
|
||||
long begin = System.nanoTime();
|
||||
Object obj = null;
|
||||
MethodNode parent = InvokeService.getParentMethodNode();
|
||||
MethodStack.record(invocation);
|
||||
if (exceptionEnable) {
|
||||
try {
|
||||
obj = invocation.proceed();
|
||||
@ -36,19 +39,19 @@ public class RunTimeHandler implements MethodInterceptor {
|
||||
graphService.addExceptionNode(exception);
|
||||
graphService.addExceptionRelation(current, exception);
|
||||
}
|
||||
MethodStack.clear();
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
obj = invocation.proceed();
|
||||
}
|
||||
long end = System.nanoTime();
|
||||
String packName = invocation.getMethod().getDeclaringClass().getPackage().getName();
|
||||
MethodNode parent = InvokeService.getParentMethodNode(packName);
|
||||
MethodNode current = InvokeService.getCurrentMethodNode(invocation, ((end - begin) / 1000000.0));
|
||||
GraphService graphService = GraphService.getInstance();
|
||||
graphService.addMethodNode(parent);
|
||||
graphService.addMethodNode(current);
|
||||
graphService.addMethodRelation(parent, current);
|
||||
MethodStack.clear();
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,27 +1,34 @@
|
||||
package cn.langpy.kotime.service;
|
||||
|
||||
|
||||
import cn.langpy.kotime.model.MethodNode;
|
||||
import cn.langpy.kotime.util.Common;
|
||||
import cn.langpy.kotime.util.MethodStack;
|
||||
import cn.langpy.kotime.util.MethodType;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Stack;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
||||
public class InvokeService {
|
||||
public static Logger log = Logger.getLogger(InvokeService.class.toString());
|
||||
|
||||
|
||||
public static MethodNode getParentMethodNode(String packName) {
|
||||
String parentClassName = "";
|
||||
String parentMothodName = "";
|
||||
StackTraceElement[] stacks = Thread.currentThread().getStackTrace();
|
||||
StackTraceElement stack = Common.filter(stacks, packName);
|
||||
if (stack != null) {
|
||||
parentClassName = stack.getClassName();
|
||||
parentMothodName = stack.getMethodName();
|
||||
public static MethodNode getParentMethodNode() {
|
||||
Stack<String> stack = MethodStack.get();
|
||||
if (null==stack) {
|
||||
MethodNode parent = new MethodNode();
|
||||
parent.setId("com.langpy.kotime.Cotroller.dispatch");
|
||||
parent.setClassName("Cotroller");
|
||||
parent.setMethodName("dispatch");
|
||||
parent.setName("Cotroller.dispatch");
|
||||
parent.setMethodType(MethodType.Others);
|
||||
return parent;
|
||||
}
|
||||
String classMethod = stack.peek();
|
||||
String[] classMethodSplit = classMethod.split("#");
|
||||
String parentClassName = classMethodSplit[0];
|
||||
String parentMothodName = classMethodSplit[1];
|
||||
MethodNode parent = new MethodNode();
|
||||
parent.setId(parentClassName + "." + parentMothodName);
|
||||
parent.setClassName(parentClassName);
|
||||
|
||||
@ -2,7 +2,6 @@ package cn.langpy.kotime.util;
|
||||
|
||||
import cn.langpy.kotime.model.MethodRelation;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -13,19 +12,6 @@ import java.util.logging.Logger;
|
||||
public class Common {
|
||||
public static Logger log = Logger.getLogger(Common.class.toString());
|
||||
|
||||
public static StackTraceElement filter(StackTraceElement[] stacks, String packName) {
|
||||
String[] packNameSplit = packName.split("\\.");
|
||||
String filter = packNameSplit.length > 1 ? packNameSplit[0] + "." + packNameSplit[1] : packNameSplit[0];
|
||||
int stacksLength = stacks.length;
|
||||
for (int i = 0; i < stacksLength; i++) {
|
||||
StackTraceElement stack = stacks[i];
|
||||
if (stack.getClassName().startsWith(filter) && !stack.getClassName().contains("$")) {
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static MethodType getMethodType(MethodInvocation pjp) {
|
||||
Class<?> targetClass = pjp.getThis().getClass();
|
||||
@ -47,28 +33,6 @@ public class Common {
|
||||
return MethodType.Others;
|
||||
}
|
||||
}
|
||||
|
||||
public static MethodType getMethodType(ProceedingJoinPoint pjp) {
|
||||
Class<?> targetClass = pjp.getTarget().getClass();
|
||||
if (targetClass.getAnnotation(Controller.class) != null || targetClass.getAnnotation(RestController.class) != null) {
|
||||
return MethodType.Controller;
|
||||
} else if (targetClass.getAnnotation(Service.class) != null) {
|
||||
return MethodType.Service;
|
||||
} else if (targetClass.getAnnotation(Repository.class) != null) {
|
||||
return MethodType.Dao;
|
||||
}
|
||||
String className = pjp.getTarget().getClass().getName().toLowerCase();
|
||||
if (className.contains("controller")) {
|
||||
return MethodType.Controller;
|
||||
} else if (className.contains("service")) {
|
||||
return MethodType.Service;
|
||||
} else if (className.contains("dao") || className.contains("mapper") || className.contains("com.sun.proxy.$Proxy")) {
|
||||
return MethodType.Dao;
|
||||
} else {
|
||||
return MethodType.Others;
|
||||
}
|
||||
}
|
||||
|
||||
public static MethodType getMethodType(String className) {
|
||||
className = className.toLowerCase();
|
||||
if (className.contains("controller")) {
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
package cn.langpy.kotime.util;
|
||||
|
||||
import cn.langpy.kotime.model.KoTimeConfig;
|
||||
import cn.langpy.kotime.config.DefaultConfig;
|
||||
|
||||
public class Context {
|
||||
|
||||
private static KoTimeConfig config;
|
||||
private static DefaultConfig config;
|
||||
|
||||
static {
|
||||
config = new KoTimeConfig();
|
||||
config = new DefaultConfig();
|
||||
config.setLogEnable(false);
|
||||
config.setKotimeEnable(true);
|
||||
config.setEnable(true);
|
||||
config.setLogLanguage("chinese");
|
||||
}
|
||||
|
||||
public static void setConfig(KoTimeConfig koTimeConfig) {
|
||||
public static void setConfig(DefaultConfig koTimeConfig) {
|
||||
config = koTimeConfig;
|
||||
}
|
||||
|
||||
public static KoTimeConfig getConfig() {
|
||||
public static DefaultConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
39
src/main/java/cn/langpy/kotime/util/MethodStack.java
Normal file
39
src/main/java/cn/langpy/kotime/util/MethodStack.java
Normal file
@ -0,0 +1,39 @@
|
||||
package cn.langpy.kotime.util;
|
||||
|
||||
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringStack;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
public class MethodStack {
|
||||
|
||||
private static ThreadLocal<Stack> threadMethods = new ThreadLocal<>();
|
||||
|
||||
public static void record(MethodInvocation pjp) {
|
||||
String className = pjp.getMethod().getDeclaringClass().getName();
|
||||
String methodName = pjp.getMethod().getName();
|
||||
Stack<String> queue = null;
|
||||
if (null==threadMethods.get()) {
|
||||
queue = new StringStack();
|
||||
}else {
|
||||
queue = threadMethods.get();
|
||||
}
|
||||
queue.add(className+"#"+methodName);
|
||||
threadMethods.set(queue);
|
||||
}
|
||||
|
||||
public static Stack get() {
|
||||
return threadMethods.get();
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
Stack<String> queue = threadMethods.get();
|
||||
if (queue==null) {
|
||||
return;
|
||||
}
|
||||
queue.pop();
|
||||
if (queue.isEmpty()) {
|
||||
threadMethods.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user