[Use] custom annotation layer method to increase service access log

This article relates to the technology are: SLF4J, JDK dynamic proxy, AspectJ, Java custom annotations, Java reflection.

background

Recent work in order to facilitate the investigation and found that the server logs, the company's service layer will have access log method, similar ---------- getXXX (String name = xujian, User user = { "name": "xujian" , "age": 20}) ---------- START, ---------- getXXX (String name = xujian, User user = { "name": "xujian", " age ": 20}) ---------- END, which method comprises the value of the parameter (that may have data response).

But the reality is to make the final print out something different, the number of dashes and some do not like the form of the value of some parameters are not the same (most of which are present in an object parameter, if the object overrides toString the method of printing the object-specific attribute value, then good, if not it will only override the full path to the print object belongs classes + hashcode), many even forget to log placeholder value to the filling, causing the log, there are no Parameter information.

Custom annotation

Consider using custom annotations, notes increased by giving way to automatically print ending log.

/**
 * 用于自动织入方法访问日志,加上该注解即可自动添加方法首尾日志
 * @author xujian
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLog {
    String[] paramsName() default {};//方法的形参列表
}

Dynamic proxy / AOP

With custom annotation, AspectJ make notes with respect to work, will automatically print log woven into the code.

/**
 * 访问日志织入,自动添加方法首尾日志
 *
 * @author xujian
 * @create 2019-01-11 10:05
 **/
@Aspect
@Component
public class AccessLogAop {
    @Pointcut(value = "@annotation(com.xxx.AccessLog)")
    public void pointcut() {
    }

    @Around(value = "pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Class targetClass = pjp.getTarget().getClass();//获取被代理的原始类
        String methodName = pjp.getSignature().getName();//获取当前调用的方法名
        Class<?>[] parameterTypes = ((MethodSignature) pjp.getSignature()).getMethod().getParameterTypes();//获取参数类型列表
        Method targetMethod = targetClass.getDeclaredMethod(methodName,parameterTypes);
        AccessLog accessLog = targetMethod.getAnnotation(AccessLog.class);
        String[] paramsName = accessLog.paramsName();//获取指定的形参列表
        Field field = targetClass.getDeclaredField("logger");
        field.setAccessible(true);
        FLogger logger = (FLogger) field.get(targetClass);//获取service声明的logger属性
        String targetMethodName = pjp.getSignature().getName();
        StringBuilder sb = new StringBuilder();
        Object[] args = pjp.getArgs();//获取参数值列表
        /*
        *拼装日志内容
        */
        for (int i=0;i<args.length;i++) {
            Object o = args[i];
            if (o == null) {
                o = "null";
            }
            Object paramValue = o;
            //判断是否是基本类型,非基本类型的话转为json字符串
            if (!o.getClass().isPrimitive() && !"java.lang.String".equals(o.getClass().getName())) {
                paramValue = JSON.toJSONString(o);
            }
            sb.append(o.getClass().getSimpleName()+" "+paramsName[i]+" = "+paramValue);
            sb.append(",");
        }
        String beginLog = "----------"+targetMethodName+"("+sb.substring(0,sb.length()-1)+")----------START";
        logger.info(beginLog);
        Object result = pjp.proceed();
        String endLog = "----------"+targetMethodName+"("+sb.substring(0,sb.length()-1)+")----------END";
        logger.info(endLog);
        return result;
    }
}

It should explain, custom annotation property there is a "parameter list" did not want to use this property, but automatically acquired by javassist byte code technology (code below), but the actual discovery is not perfect, so we abandoned used.

/**
     * 字节码技术来获取方法形参列表,结果不太完美(已废弃)
     * @param cls
     * @param clazzName
     * @param methodName
     * @return
     * @throws NotFoundException
     */
    @Deprecated
    private List<String> getParamNames(Class cls, String clazzName, String methodName) throws NotFoundException {
        List<String> result=new ArrayList<>();
        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(cls);
        pool.insertClassPath(classPath);
        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            // exception
        }
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        CtClass[] paramsType = cm.getParameterTypes();
        for (int i = 0; i < paramsType.length; i++){
            result.add(attr.variableName(i + pos));
        }
        return result;
    }

use

Very simple to use, you just need to Logger logger objects at your service inside the class declaration SLF4J, and then annotate to customize on the inside of your service method, as follows:

@Service
public class TemplateMappingServiceImpl implements TemplateMappingService {
    private static final Logger logger = LoggerFactory.getLogger(TemplateMappingServiceImpl.class);
    @Override
    @AccessLog(paramsName = {"mainContractId","signerId","isAgent","templateVar","calculatedVars"})
    public Map<String,String> getTemplateMapping(Integer mainContractId, Integer signerId, boolean isAgent, Set<String> templateVar, Map<String,String> calculatedVars) 
}

to sum up

Although this log format to avoid the confusion and forget the print log problem, but there are many places you can continue to improve and optimize. Such as

  1. Abnormalities may increase print log
  2. Because a proxy, so in this class directly inside the method call, in fact, this is the final call of the object (the original object being proxied) approach and did not enhance the relevant code, so you can not print out the log, we can address this situation use proxy class to call.

Although not very complicated, but it would be a complete AOP + practice notes from the definition.


The relevant code github: custom annotation layer method to increase service access log

Guess you like

Origin www.cnblogs.com/xuxiaojian/p/11453724.html