其中Binaries是编译好的版本,Source是源码包
tgz是Linux版本,zip是windows版本
变更JMeter语言
找到JMeter配置文件 D:\tools\apache-jmeter-5.5\bin\jmeter.properties
修改语言(默认英文):
language=zh_CN
需要开启配置:test.function.cost=false
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Configuration;
/**
* 打印方法调用耗时和方法入参
* @author caimeng
* @date 2023/06/13 10:58
*/
@Slf4j
@Aspect
@Configuration
@ConditionalOnExpression("${test.function.cost:false}")
public class ServiceDigestAop {
public ServiceDigestAop() {
}
/**
* 切面:方法调用时间和方法入参
* @param joinPoint 切点
* @return 方法调用返回数据
* @throws Throwable 调用失败返回的异常
*/
@Around("execution (* com.gomain..*.service.impl.*.*(..))" +
"||execution(* com.gomain.api.sign.controller.ShZhStandardController.*(..))" +
"||execution(* com.gomain.sign.feign.SignFeign.*(..))" +
"||execution(* com.gomain.seal.feign.SealQueryFeign.*(..))" +
"||execution(* com.gomain.api.mq.producer.Producer.*(..))" +
"||execution(* com.gomain.calculate.feign.CalculateServiceFeign.*(..))")
public Object timeAround(ProceedingJoinPoint joinPoint) throws Throwable{
Object obj = null;
Object[] args = joinPoint.getArgs();
long startTime = System.currentTimeMillis();
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
String methodName = signature.getDeclaringTypeName() + "_" + signature.getName();
this.printBeginLog(methodName, args);
try {
obj = joinPoint.proceed(args);
} catch (Throwable var10) {
this.printErrorLog(methodName, args, var10);
throw var10;
}
long endTime = System.currentTimeMillis();
this.printExecTime(methodName, args, startTime, endTime);
return obj;
}
/**
* 是否基本数据类型,或字符串类型
* @param clazz 待检测类
* @return 是否基本类型或字符串类型
*/
private boolean isPrimitive(Class<?> clazz) {
return clazz.isPrimitive() || clazz == String.class;
}
/**
* 是否短字符串
* @param str 待检测字符串
* @return 是否短字符串
*/
private boolean isShortString(String str) {
return str.length() <= 100;
}
/**
* 打印方法开始执行时间点
* @param methodName 方法名
* @param args 方法参数
*/
private void printBeginLog(String methodName, Object[] args) {
String param = this.generateParamDigest(args);
log.info("[{}] begin param [{}]", methodName, param);
}
/**
* 打印方法成功执行后时间点
* @param methodName 方法名
* @param args 方法参数
*/
private void printExecTime(String methodName, Object[] args, long startTime, long endTime) {
String param = this.generateParamDigest(args);
log.info("[{}] success param [{}] cost {} ms", methodName, param, endTime - startTime);
}
/**
* 方法执行失败,打印参数和错误消息
* @param methodName 方法名
* @param args 方法参数
*/
private void printErrorLog(String methodName, Object[] args, Throwable e) {
String param = this.generateParamDigest(args);
String errormsg = e.getMessage().replaceAll("\\s*", "_");
log.error("[{}] error param [{}] errmsg [{}]", methodName, param, errormsg);
}
/**
* 简单方法参数
* @param args 方法参数
* @return 方法参数的简易拼接
*/
private String generateParamDigest(Object[] args) {
if (args == null) {
return "args is null !";
} else {
StringBuffer argString = new StringBuffer();
Object[] var3 = args;
int var4 = args.length;
for(int var5 = 0; var5 < var4; ++var5) {
Object arg = var3[var5];
if (arg != null) {
if (this.isPrimitive(arg.getClass())) {
if (this.isShortString(arg.toString())) {
argString.append(arg).append(",");
} else {
argString.append(arg.getClass().getName()).append(",");
}
} else {
argString.append(arg.getClass().getName()).append(",");
}
}
}
return argString.toString();
}
}
}
<appender name="digestLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--encoder 默认配置为PatternLayoutEncoder-->
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
<!--滚动策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--路径-->
<fileNamePattern>${LOG_PATH}/digest-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>500MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--MaxHistory指的是文件数量,超过MaxHistory数量才会删除日志文件 -->
<maxHistory>10</maxHistory>
<!--TimeBasedRollingPolicy下logback 启动项目时候 默认不删除多余的文件-->
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
</appender>
...
<logger name="com.gomain.api.configuration.ServiceDigestAop" level="info">
<appender-ref ref="digestLog"/>
</logger>
cat digest-2023-06-13.0.log | grep 'StampServiceImpl_stampApi' | sed -E 's/^.*StampServiceImpl_stampApi.*(cost * ms)$/\1/' | awk '{print $14}'
第8位是traceId,第14位是耗时
取平均值
cat data | awk 'BEGIN {sum = 0;line = 0} {sum += $1;line+=1} END {printf "NR=%d,Average=%3.3fms\n",line,sum*2/line}'
以上不合适,以下面的为准
接口调用耗时
cat digest.log | grep 'ShZhStandardController_genCentralizedSignatureSTD' | awk '{if($14!=""){print $14}}'| awk 'BEGIN {sum = 0;line = 0} {sum += $1;line+=1} END {printf "line=%d,Average=%3.3fms\n",line,sum/line}'