话不多,直接刚
需求
在某个可以被动态代理的方法上加上某个注解,实现 业务数据 埋点操作;
使用方式如下
枚举类,维护打印日志规则
/**
* 最多支持打印5个参数
*/
public enum EBusinessOPT {
/**
* 业务线,操作,具体业务 参数 key
*/
TEST("ORDER","TEST",
Arrays.asList("userName","userAge","userPhone","testabc"));
private String optCode;
private String optDesc;
private List<String> logContentValueNames;
EBusinessOPT(String optCode, String optDesc, List<String> logContent) {
this.optDesc = optDesc;
this.optCode = optCode;
this.logContentValueNames = logContent;
}
public String getOptCode() {
return optCode;
}
public String getOptDesc() {
return optDesc;
}
public List<String> getLogContent() {
return logContentValueNames;
}
}
在目标方法上使用
如果需要使用方法参数作为日志打印目标,则需要如下设置。方法参数顺序跟 使用的 日志规约 枚举对象 的logContentValueNames要有业务对应关系!
@RequestMapping("/test2")
@BPoint(key = EBusinessOPT.TEST, valueNames = {"#user.name","#user.age","#user.phone","#testabc"})
public String test2(User user,String testabc) {
logger.info("-------------test2---------------");
return "test2";
}
结果演示
![](https://img.haomeiwen.com/i15253776/558527de89213929.png)
![](https://img.haomeiwen.com/i15253776/230a863b9a083b73.png)
分割线------------------------------------------------------------------------------------------------------------------------------------------------------
具体实现
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
自定义注解类
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BPoint {
EBusinessOPT key();
String[] valueNames();
}
切面逻辑,这里以before为例,具体用哪种增强方式,可以按需求自行定义
@Aspect
@Component
public class BPointAspect {
private Logger log = LoggerFactory.getLogger(BPointAspect.class);
@Pointcut("@annotation(com.fc.nacosdemo.bpoint.annotation.BPoint)")
public void bpPointCut() {}
@Before("bpPointCut()")
public void thirdBindInfoModifyBefore(JoinPoint joinPoint) {
// 将参数值设置到注解属性上
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
BPoint annotation = signature.getMethod().getAnnotation(BPoint.class);
String[] valueNames = annotation.valueNames();
EBusinessOPT key = annotation.key();
if (valueNames == null || key == null) {
return;
}
Object[] values = new Object[valueNames.length];
// 获取方法参数名称
String[] parameterNames = signature.getParameterNames();
// 将方法参数名称跟对应的值 封装到 map
Map<String, Object> paramMap = new HashMap<String, Object>();
for (int i = 0; i < parameterNames.length; i++) {
String parameterName = parameterNames[i];
paramMap.put(parameterName, joinPoint.getArgs()[i]);
}
for (int i = 0; i < valueNames.length; i++) {
String vname = valueNames[i];
if (!vname.startsWith("#")) {
values[i] = vname;
continue;
}
vname = vname.substring(1, vname.length());
if (vname.indexOf(".") == -1) {
//未包含层级
Object value = paramMap.get(vname);
if (value == null)
log.error("BPoint 配置 出错 ! , Method is {}, {} not find ! , 跟方法参数名称未匹配! ",
signature.getMethod().getClass().getName() + ":" + signature.getMethod().getName(), vname);
values[i] = value;
continue;
}
// 具有层级结构
String[] names = vname.split("\\.");
LinkedList<String> strings = new LinkedList<String>();
for (String name : names) {
strings.addLast(name);
}
strings.removeFirst();
Object v = getV(paramMap.get(names[0]), strings);
values[i] = v;
}
// 打印日志
bp(key, values);
}
// 递归获取层级参数值
private Object getV(Object origin, LinkedList<String> names) {
if(names.size() == 0){
return origin;
}
String fieldName = names.getFirst();
Class<?> aClass = origin.getClass();
try {
Field declaredField = aClass.getDeclaredField(fieldName);
declaredField.setAccessible(true);
origin = declaredField.get(origin);//name
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
names.removeFirst();
return getV(origin, names);
}
// 埋点日志打印,最多支持5个额外业务参数打印
private void bp(EBusinessOPT eBusinessOPT, Object... values) {
String date = LocalDate.now().toString();
String time = LocalTime.now().toString();
String datetime = date + " " + time;
List<String> logContent = eBusinessOPT.getLogContent();
StringBuilder sb = new StringBuilder();
for (String s2 : logContent) {
sb.append("\"").append(s2).append("\"").append(":\"{}\",");
}
sb.deleteCharAt(sb.length() - 1);
sb.append("}");
// log.info("{\"LOGLOG\":\"{}\",\"OPTLOG\":\"{}\",\"DATE-TIME\":\"{}\"," + sb.toString(),
// eBusinessOPT.getOptCode(), eBusinessOPT.getOptDesc(), datetime, values);
switch (values.length) {
case 1:
log.info("{\"LOGLOG\":\"{}\",\"OPTLOG\":\"{}\",\"DATE-TIME\":\"{}\"," + sb.toString(),
eBusinessOPT.getOptCode(), eBusinessOPT.getOptDesc(), datetime, values[0]);
break;
case 2:
log.info("{\"LOGLOG\":\"{}\",\"OPTLOG\":\"{}\",\"DATE-TIME\":\"{}\"," + sb.toString(),
eBusinessOPT.getOptCode(), eBusinessOPT.getOptDesc(), datetime, values[0], values[1]);
break;
case 3:
log.info("{\"LOGLOG\":\"{}\",\"OPTLOG\":\"{}\",\"DATE-TIME\":\"{}\"," + sb.toString(),
eBusinessOPT.getOptCode(), eBusinessOPT.getOptDesc(), datetime, values[0], values[1], values[2]);
break;
case 4:
log.info("{\"LOGLOG\":\"{}\",\"OPTLOG\":\"{}\",\"DATE-TIME\":\"{}\"," + sb.toString(),
eBusinessOPT.getOptCode(), eBusinessOPT.getOptDesc(), datetime, values[0], values[1], values[2], values[3]);
break;
case 5:
log.info("{\"LOGLOG\":\"{}\",\"OPTLOG\":\"{}\",\"DATE-TIME\":\"{}\"," + sb.toString(),
eBusinessOPT.getOptCode(), eBusinessOPT.getOptDesc(), datetime, values[0], values[1], values[2], values[3], values[4]);
break;
}
}
}
// 具体业务日志打印 可以用异步优化;
// 结合logback 日志收集 展示 ,待续....
网友评论