add a stack to optimize the code about how to get parent method

This commit is contained in:
huoyo 2021-08-16 23:03:44 +08:00
parent df45e8c4dd
commit 218e4908c8
10 changed files with 221 additions and 111 deletions

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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")) {

View File

@ -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;
}

View 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();
}
}
}