AOP日志记录

/ springboot / 2 条评论 / 1831浏览

AOP记录请求/响应日志

导包

    <!--aspectj aop切面-->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
    </dependency>

操作日志输出1.0版本

@Slf4j
@Aspect
@Configuration
public class LogRecordAspect {

    @Autowired
    private HttpServletRequest request;
    /**
     * 配置切入点表达式
     */
    @Pointcut("execution(* io.imwj.threadlocal.controller..*(..))")
    public void webData() {
    }

    @Around(value = "webData()")
    public Object aroundWebData(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long millis = System.currentTimeMillis();
        Object result = null;
        Exception ex = null;
        try {
            result = proceedingJoinPoint.proceed();
        } catch (Exception e) {
            ex = e;
            throw e;
        } finally {
            handleLog(proceedingJoinPoint, ex, result, millis);
        }
        return result;
    }

    /**
     * 处理日志
     */
    protected void handleLog(final JoinPoint joinPoint, final Exception e, final Object resultData, long millis) {
        try {
            //请求参数
            String params = JSONUtil.toJsonStr(request.getParameterMap());
            // 返回参数
            String resStr = JSONUtil.toJsonStr(resultData);
            //请求地址
            String url = request.getRequestURI();
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            // 设置请求方式
            String method = request.getMethod();
            long ms = System.currentTimeMillis() - millis;
            String info = "【" + method + "】,耗时:[" + ms + "],url:【" + url + "】," +
                    "method:[" + className + "." + methodName + "()],param:" + params + ",";
            //返回结果
            if (e != null) {
                info += "Exception:[" + e.getMessage() + "]";
                log.info(info);
            } else {
                info += "resultData:" + (resStr.length() > 600 ? "数据太长不打印" : resStr);
                log.info(info);
            }
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }
}

操作日志输出2.0版本

/**
 * 操作日志记录处理
 * @author langao
 */
@Aspect
@Component
@Slf4j
public class LogAspect {

    @Autowired
    private HttpServletRequest request;

    /**
     * 配置切入点表达式
     */
    @Pointcut("execution(* com.zhitengda.controller..*(..))")
    public void webData() {
    }

    @Around(value = "webData()")
    public Object aroundWebData(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long millis = System.currentTimeMillis();
        Object result = null;
        Exception ex = null;
        try {
            result = proceedingJoinPoint.proceed();
        } catch (Exception e) {
            ex = e;
            throw e;
        } finally {
            handleLog(proceedingJoinPoint, ex, result, millis);
        }
        return result;
    }

    /**
     * 处理日志
     */
    protected void handleLog(final JoinPoint joinPoint, final Exception e, final Object resultData, long millis) {
        try {
            //请求参数
            String params = "";
            if("GET".equals(request.getMethod())){
                params = JSONUtil.toJsonStr(request.getParameterMap());
            }else{
                params = getRequestParams(joinPoint, request.getMethod());
            }
            // 返回参数
            String resStr = JSONUtil.toJsonStr(resultData);
            //请求地址
            String url = request.getRequestURI();
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            // 设置请求方式
            String method = request.getMethod();
            long ms = System.currentTimeMillis() - millis;
            String info = "【" + method + "】,耗时:[" + ms + "],url:【" + url + "】," +
                    "method:[" + className + "." + methodName + "()],param:" + params + ",";
            //返回结果
            if (e != null) {
                info += "Exception:[" + e.getMessage() + "]";
                log.info(info);
            }else if(resStr != null){
                info += "resultData:" + (resStr.length() > 600 ? "数据太长不打印" : resStr);
                log.info(info);
            } else {
                info += "resultData:{}";
                log.info(info);
            }
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }

    /**
     * 获取请求的参数
     *
     * @throws Exception 异常
     */
    private String getRequestParams(JoinPoint joinPoint, String method) throws Exception {
        String params = "";
        if (HttpMethod.PUT.name().equals(method) || HttpMethod.POST.name().equals(method)) {
            params = argsArrayToString(joinPoint.getArgs());
        } else {
            String decode = "";
            if (request.getQueryString() != null) {
                decode = URLDecoder.decode(request.getQueryString(), "utf-8");
            }
            params = "{" + decode + "}";
        }
        return params;
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (int i = 0; i < paramsArray.length; i++) {
                if (!isFilterObject(paramsArray[i])) {
                    Object jsonObj = JSON.toJSON(paramsArray[i]);
                    params += (jsonObj != null ? jsonObj.toString() : "") + " ";
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象,则返回true;否则返回false。
     */
    public boolean isFilterObject(final Object o) {
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }
}

logback.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="30 minutes">
    <property name="logDir" value="${catalina.base}/logs"/>
    <!-- 打印到控制台 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date{yyyy-MM-dd HH🇲🇲ss.SSS} [%thread] %-5level-%logger{5} - %msg%n</pattern>
        </encoder>
    </appender>
    <appender name="debuglog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建 -->
        <file>${logDir}/debug.log</file>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${logDir}/debug_%d{yyyy-MM-dd}.log%i.zip</fileNamePattern>
            <!-- 限制文件最大保存时间为15天; 15*24=360 -->
            <maxHistory>360</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- 当文件大小超过60M时触发滚动,这里设置60M -->
                <maxFileSize>60MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%date{yyyy-MM-dd HH🇲🇲ss.SSS} [%thread] %-5level-%logger{5} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="infolog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建 -->
        <file>${logDir}/info.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${logDir}/info_%d{yyyy-MM-dd-a}.log%i.zip</fileNamePattern>
            <!-- 限制文件最大保存时间为15天; 15*24=360 -->
            <maxHistory>360</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- 当文件大小超过60M时触发滚动,这里设置60M -->
                <maxFileSize>60MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%date{yyyy-MM-dd HH🇲🇲ss.SSS} [%thread] %-5level-%logger{5} - %msg%n</pattern>
        </encoder>
        <!-- LevelFilter: 级别过滤器,根据日志级别进行过滤 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <!-- 用于配置符合过滤条件的操作 ACCEPT:日志会被立即处理,不再经过剩余过滤器 -->
            <onMatch>ACCEPT</onMatch>
            <!-- 用于配置不符合过滤条件的操作 DENY:日志将立即被抛弃不再经过其他过滤器 -->
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <appender name="errorlog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建 -->
        <file>${logDir}/error.log</file>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>WARN</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${logDir}/error_%d{yyyy-MM-dd}.log%i.zip</fileNamePattern>
            <!-- 限制文件最大保存时间为15天; 15*24=360 -->
            <maxHistory>360</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <!-- 当文件大小超过60M时触发滚动,这里设置60M -->
                <maxFileSize>60MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%date{yyyy-MM-dd HH🇲🇲ss.SSS} [%thread] %-5level-%logger{5} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="stdout" />
        <appender-ref ref="debuglog"/>
        <appender-ref ref="infolog"/>
        <appender-ref ref="errorlog"/>
    </root>

</configuration>

后话