项目基本日志输出

先看效果

功能介绍

在控制器头上打上注解,设置两个参数作者和功能描述

运行后自动打印出必要的日志,排查错误时能清晰的定位。

主要包括sql语句,出入参数,时间与耗时,描述,路径与文件信息,线程号。

建立类

首先写个注解,设置俩参数

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoLog {

    String description() default "";

    String author() default "";
}

 然后利用切面给注解标注的方法加功能

这一个类需要导入相关的包,这里用的时Hutool的Log工具,用的SpringAOP。@RequiredArgsConstructor这个注解生成构造。

写这个需要先了解一下Spring的切面编程,对业务代码进行增强。

首先搭好这个架子,doBefore中获取开始时间,其他两个是在结束时执行打印日志的功能

@Aspect
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class SystemLogAspect {

    private final static Log log = LogFactory.get();
    private final static String INFO = "info", ERROR = "error";
    private final HttpServletRequest request;
    private long time;

    /**
     * controller层切点,注解方式
     */
    @Pointcut(value = "@annotation(zs.util.anno.AutoLog)")
    public void controllerAspect() {
    }

    /**
     * 前置通知 用于拦截Controller层记录用户的操作的开始时间
     */
    @Before(value = "controllerAspect()")
    public void doBefore(JoinPoint joinPoint) {
        time = System.currentTimeMillis();
    }

    /**
     * 后置正常通知 用于拦截Controller层操作
     *
     * @param joinPoint 切点对象
     * @param result    参数返回数据
     */
    @AfterReturning(value = "controllerAspect()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, String result) throws Exception {
        build(INFO, joinPoint, result, null);
    }

    /**
     * 后置异常通知(在方法执行之后返回) 用于拦截Controller层操作
     *
     * @param joinPoint 切点对象
     * @param ex        异常
     */
    @AfterThrowing(value = "controllerAspect()", throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint, Exception ex) throws Exception {
        build(ERROR, joinPoint, null, ex);
    }

    /**
     * 获取注解中对方法的描述信息,也就是切点方法头上注解的参数
     */
    private AnnotationInfo getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
        String methodName = joinPoint.getSignature().getName();
        Class<?> classTarget = joinPoint.getTarget().getClass();
        Class<?>[] par = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        Method objMethod = classTarget.getMethod(methodName, par);

        String description = objMethod.getAnnotation(AutoLog.class).description();
        String author = objMethod.getAnnotation(AutoLog.class).author();
        return new AnnotationInfo().description(description).author(author);
    }


    @Data
    @Accessors(fluent = true)
    private class AnnotationInfo {
        private String description;
        private String author;
    }
}

具体构建方法build,从JoinPoint中拿切点方法信息,从request里拿路径,再获取结束时间,再拼接上注解中获取的参数。

private void build(String logType, JoinPoint joinPoint, String result, Exception ex) throws Exception {

        StringBuilder builder = new StringBuilder("\n");

        /* 日志请求url */
        String logUrl = request.getRequestURI();
        if (StrUtil.isNotBlank(logUrl)) {
            builder.append("请求路径: ").append(logUrl).append("\t");
        }

        /* 项目路径 */
        String classPath = joinPoint.getTarget().getClass().getPackage().getName() + "." + joinPoint.getSignature().toShortString();
        if (StrUtil.isNotBlank(classPath)) {
            builder.append("调用方法 ").append(classPath).append("\n");
        }

        /* 获取注解 */
        AnnotationInfo annotationInfo = getControllerMethodDescription(joinPoint);

        /* 方法描述 */
        String description = annotationInfo.description();
        if (StrUtil.isNotBlank(description)) {
            builder.append("描述: ").append(description);
        }
        /* 方法作者 */
        String author = annotationInfo.author();
        if (StrUtil.isNotBlank(author)) {
            builder.append("——").append(author).append("\n");
        }

        /* 请求参数 */
        String logParam = ServletUtil.getParamMap(request).isEmpty()
                ? HttpUtil.decode(ArrayUtil.toString(joinPoint.getArgs()), CharsetUtil.UTF_8)
                : ServletUtil.getParamMap(request).toString();
        logParam = StrUtil.replaceChars(logParam, "\r\n\t ", "");
        if (StrUtil.isNotBlank(logParam)) {
            builder.append("请求参数: ").append(logParam).append("\n");
        }


        /* info 日志返回数据 */
        if (INFO.equals(logType)) {
            String logResult = StrUtil.isBlank(result) ? "" : result;
            if (StrUtil.isNotBlank(logResult)) {
                builder.append("返回结果: ").append(logResult).append("\n");
            }
            builder.append("耗时: ").append(System.currentTimeMillis() - time + "ms").append("\n");
            log.info(builder.toString());
        }
        /* error 日志异常信息 */
        if (ERROR.equals(logType)) {
            String logResult = getErrorOf(ex).orElse("");
            if (StrUtil.isNotBlank(logResult)) {
                builder.append("错误信息: ").append(logResult);
            }
            log.error(builder.toString());
        }
    }

 

错误日志方法

   /**
     * 提取error信息中关键信息
     */
    private Optional<String> getErrorOf(Exception ex) {
        if (StrUtil.isBlank(ex.toString())) {
            return Optional.empty();
        }
        StringBuilder builder = new StringBuilder(ExceptionUtil.getMessage(ex) + "\n");
        StackTraceElement[] stackTrace = ex.getStackTrace();
        for (StackTraceElement traceElement : stackTrace) {
            String checkErrMsg = traceElement.toString();
            if (checkErrMsg.contains("zs.") && !checkErrMsg.contains("$$")) {
                builder.append(traceElement.toString()).append("\n");
            }
        }
        return Optional.of(builder.toString());
    }

 把sql语句写错一点

改配置

以上日志并不包括打印sql语句,我这用的mybatis,经过很多标签拼装sql语句和#{}拼接值,最便捷的办法就是用mybatis带的工具打印,找到yml文件发现需要配置configuration.log_impl,默认实现org.apache.ibatis.logging.stdout.StdOutImpl,原来的效果不好,看着乱,点进去发现是实现org.apache.ibatis.logging.Log接口,于是我自己写一个。

public class MybatisLog implements Log {

    public MybatisLog(String clazz) {
    }

    @Override
    public boolean isDebugEnabled() {
        return true;
    }

    @Override
    public boolean isTraceEnabled() {
        return false;
    }

    @Override
    public void error(String s, Throwable e) {
        System.err.println(s);
        e.printStackTrace(System.err);
    }

    @Override
    public void error(String s) {
        System.err.println(s);
    }

    @Override
    public void debug(String s) {
        if(s.indexOf("Preparing")==5){
            System.out.println("SQL语句: "+s.split("Preparing: ")[1]);
        }
    }

    @Override
    public void trace(String s) {
        System.out.println(s);
    }

    @Override
    public void warn(String s) {
        System.out.println(s);
    }
}

这样就打印出纯净的sql语句,带#{}的地方拼接'?',我分页用的pagehelper,内部直接拼的Limit字符串,所以参数没有'?'

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值