zoukankan      html  css  js  c++  java
  • springboot aop + logback + 统一异常处理 打印日志

    1.src/resources路径下新建logback.xml

    • 控制台彩色日志打印
    • info日志和异常日志分不同文件存储
    • 每天自动生成日志
    • 结合myibatis方便日志打印(debug模式)
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration debug="false">
    
    
        <!--定义日志文件的存储地址 可以在LogBack 的配置中使用相对路径-->
        <property name="LOG_HOME" value="logs" />
    
        <!-- 彩色日志 -->
        <!-- 彩色日志依赖的渲染类 -->
        <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
        <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
        <conversionRule conversionWord="wEx"
                        converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
        <!-- 彩色日志格式 -->
        <property name="CONSOLE_LOG_PATTERN"
                  value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
        <!-- Console 输出设置 -->
        <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
                <charset>utf8</charset>
            </encoder>
        </appender>
    
        <!-- 按照每天生成日志文件 -->
        <appender name="FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!--日志文件输出的文件名-->
                <FileNamePattern>${LOG_HOME}/category-server-log.%d{yyyy-MM-dd}.log</FileNamePattern>
                <!--日志文件保留天数-->
                <MaxHistory>30</MaxHistory>
            </rollingPolicy>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            </encoder>
            <!--日志文件最大的大小
            <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
                <MaxFileSize>10MB</MaxFileSize>
            </triggeringPolicy>-->
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>WARN</level>
                <onMatch>DENY</onMatch>
                <onMismatch>NEUTRAL</onMismatch>
            </filter>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>ERROR</level>
                <onMatch>DENY</onMatch>
                <onMismatch>NEUTRAL</onMismatch>
            </filter>
    
        </appender>
    
        <!-- 出错日志 appender  -->
        <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!-- 按天回滚 daily -->
                <!-- log.dir 在maven profile里配置 -->
                <FileNamePattern>${LOG_HOME}/category-server-error-log.%d{yyyy-MM-dd}.log</FileNamePattern>
                <!-- 日志最大的历史 60天 -->
                <maxHistory>60</maxHistory>
            </rollingPolicy>
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>WARN</level>
            </filter>
            <encoder>
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            </encoder>
        </appender>
        <!--myibatis log configure-->
        <logger name="com.apache.ibatis" level="TRACE"/>
        <logger name="java.sql.Connection" level="DEBUG"/>
        <logger name="java.sql.Statement" level="DEBUG"/>
        <logger name="java.sql.PreparedStatement" level="DEBUG"/>
        <!-- 日志输出级别 -->
        <root level="INFO">
            <appender-ref ref="CONSOLE" />
            <appender-ref ref="FILE" />
            <appender-ref ref="ERROR_FILE" />
        </root>
    </configuration>
    

    2.定义一个log实体 方便维护和后续的扩展

    package com.wdcloud.categoryserver.log;
    
    import com.wdcloud.categoryserver.common.entity.User;
    
    /**
     * @describe: 日志打印实体
     * @author: zhuchunwang
     * @date: 2018/5/29 17:40
     * @version: 1.0
     */
    public class LogEntity {
        private long id;
        /**
         * 请求地址
         */
        private String url;
        /**
         * 请求IP
         */
        private String ip;
        /**
         * 请求方式
         */
        private String httpMethod;
        /**
         * 请求类,方法
         */
        private String classMethod;
        /**
         * 方法参数
         */
        private String args;
        /**
         * 请求参数
         */
        private String reqParams;
        /**
         * 响应参数
         */
        private String respParams;
        /**
         * 响应时间
         */
        private long spendTime;
        /**
         * 日志类型(web、service)
         */
        private String logType;
        /**
         * 用户信息
         */
        private User user;
    
    
        public long getId() {
            return id;
        }
    
        public void setId(long id) {
            this.id = id;
        }
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
        public String getIp() {
            return ip;
        }
    
        public void setIp(String ip) {
            this.ip = ip;
        }
    
        public String getHttpMethod() {
            return httpMethod;
        }
    
        public void setHttpMethod(String httpMethod) {
            this.httpMethod = httpMethod;
        }
    
        public String getClassMethod() {
            return classMethod;
        }
    
        public void setClassMethod(String classMethod) {
            this.classMethod = classMethod;
        }
    
        public String getArgs() {
            return args;
        }
    
        public void setArgs(String args) {
            this.args = args;
        }
    
        public String getReqParams() {
            return reqParams;
        }
    
        public void setReqParams(String reqParams) {
            this.reqParams = reqParams;
        }
    
        public String getRespParams() {
            return respParams;
        }
    
        public void setRespParams(String respParams) {
            this.respParams = respParams;
        }
    
        public long getSpendTime() {
            return spendTime;
        }
    
        public void setSpendTime(long spendTime) {
            this.spendTime = spendTime;
        }
    
        public String getLogType() {
            return logType;
        }
    
        public void setLogType(String logType) {
            this.logType = logType;
        }
    
        public User getUser() {
            return user;
        }
    
        public void setUser(User user) {
            this.user = user;
        }
    }
    
    

    3.定义一个controller 的切面

    package com.wdcloud.categoryserver.log;
    
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.wdcloud.categoryserver.common.constant.AppConstants;
    import com.wdcloud.categoryserver.utils.AppUtil;
    import com.wdcloud.categoryserver.utils.JsonUtil;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * @describe: 实现controller的日志切面
     * @author: zhuchunwang
     * @date: 2018/5/29 17:40
     * @version: 1.0
     */
    @Aspect
    @Component
    @Order(1)
    public class ControllerLogAspect {
        private Logger logger = LoggerFactory.getLogger(this.getClass());
        ThreadLocal<Long> startTime = new ThreadLocal<>();
        ThreadLocal<LogEntity> webLogThreadLocal = new ThreadLocal<>();
        /**
         * 定义一个切入点.
         * 解释下:
         * <p>
         * ~ 第一个 * 代表任意修饰符及任意返回值.
         * ~ 第二个 * 任意包名
         * ~ 第三个 * 代表任意方法.
         * ~ 第四个 * 定义在web包或者子包
         * ~ 第五个 * 任意方法
         * ~ .. 匹配任意数量的参数.
         */
        @Pointcut("execution(* com.wdcloud.categoryserver.business.*.controller.*.*(..))")
        public void serviceAspect() {
        }
        @Before("serviceAspect()")
        public void doBefore(JoinPoint joinPoint) {
            // 接收到请求,记录请求内容
            startTime.set(System.currentTimeMillis());
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            webLogThreadLocal.set(new LogEntity());
            webLogThreadLocal.get().setHttpMethod(request.getMethod());
            webLogThreadLocal.get().setClassMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            webLogThreadLocal.get().setUrl(request.getRequestURL().toString());
            webLogThreadLocal.get().setIp(request.getRemoteAddr());
            webLogThreadLocal.get().setArgs(AppUtil.getArgs(joinPoint));
            webLogThreadLocal.get().setLogType(AppConstants.LOG_TYPE_HTTP);
            webLogThreadLocal.get().setReqParams(JsonUtil.objectToJson(request.getParameterMap()));
            webLogThreadLocal.get().setUser(AppUtil.getUser(request));
        }
    
        /**
         * 异常通知 用于拦截service层记录异常日志
         *
         * @param
         * @param
         */
        @AfterReturning(returning = "result", pointcut = "serviceAspect()")
        public void doAfterReturning(Object result) {
            // 处理完请求,返回内容
            ObjectMapper mapper = new ObjectMapper();
            try {
                webLogThreadLocal.get().setRespParams(mapper.writeValueAsString(result));
            } catch (JsonProcessingException e) {
                logger.error(AppUtil.getExceptionDetail(e));
            }
            webLogThreadLocal.get().setSpendTime(System.currentTimeMillis() - startTime.get());
            try {
                logger.info(">>>"+mapper.writeValueAsString(webLogThreadLocal.get()));
            } catch (JsonProcessingException e) {
    	        logger.error(AppUtil.getExceptionDetail(e));
            }
        }
    }
    
    

    4.定义一个供dubbo调用的service切面

    package com.wdcloud.categoryserver.log;
    
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.wdcloud.categoryserver.common.constant.AppConstants;
    import com.wdcloud.categoryserver.utils.AppUtil;
    import com.wdcloud.categoryserver.utils.JsonUtil;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * @describe: 实现controller的日志切面
     * @author: zhuchunwang
     * @date: 2018/5/29 17:40
     * @version: 1.0
     */
    @Aspect
    @Component
    @Order(1)
    public class ServiceLogAspect {
        private Logger logger = LoggerFactory.getLogger(this.getClass());
        ThreadLocal<Long> startTime = new ThreadLocal<>();
        ThreadLocal<LogEntity> webLogThreadLocal = new ThreadLocal<>();
        /**
         * 定义一个切入点.
         * 解释下:
         * <p>
         * ~ 第一个 * 代表任意修饰符及任意返回值.
         * ~ 第二个 * 任意包名
         * ~ 第三个 * 代表任意方法.
         * ~ 第四个 * 定义在web包或者子包
         * ~ 第五个 * 任意方法
         * ~ .. 匹配任意数量的参数.
         */
        @Pointcut("execution(* com.wdcloud.categoryserver.front.*.service.*.*(..))")
        public void serviceAspect() {
        }
        @Before("serviceAspect()")
        public void doBefore(JoinPoint joinPoint) {
            // 接收到请求,记录请求内容
            startTime.set(System.currentTimeMillis());
            webLogThreadLocal.set(new LogEntity());
            webLogThreadLocal.get().setClassMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
            webLogThreadLocal.get().setArgs(AppUtil.getArgs(joinPoint));
            webLogThreadLocal.get().setLogType(AppConstants.LOG_TYPE_DUBBO);
        }
    
        /**
         * 异常通知 用于拦截service层记录异常日志
         *
         * @param
         * @param
         */
        @AfterReturning(returning = "result", pointcut = "serviceAspect()")
        public void doAfterReturning(Object result) {
            // 处理完请求,返回内容
            ObjectMapper mapper = new ObjectMapper();
            try {
                webLogThreadLocal.get().setRespParams(mapper.writeValueAsString(result));
            } catch (JsonProcessingException e) {
                logger.error(AppUtil.getExceptionDetail(e));
            }
            webLogThreadLocal.get().setSpendTime(System.currentTimeMillis() - startTime.get());
            try {
                logger.info(">>>"+mapper.writeValueAsString(webLogThreadLocal.get()));
            } catch (JsonProcessingException e) {
                logger.error(AppUtil.getExceptionDetail(e));
            }
        }
    }
    
    

    5.统一异常处理

    package com.wdcloud.categoryserver.common.exception;
    
    import com.wdcloud.categoryserver.common.constant.AppConstants;
    import com.wdcloud.categoryserver.common.constant.CodeConstants;
    import com.wdcloud.categoryserver.common.entity.BaseView;
    import com.wdcloud.categoryserver.log.LogEntity;
    import com.wdcloud.categoryserver.utils.AppUtil;
    import com.wdcloud.categoryserver.utils.JsonUtil;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.http.converter.HttpMessageNotReadableException;
    import org.springframework.web.HttpMediaTypeNotSupportedException;
    import org.springframework.web.bind.MissingServletRequestParameterException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartException;
    import org.springframework.web.multipart.support.MissingServletRequestPartException;
    
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * @describe: 全局异常处理
     * @author: zhuchunwang
     * @date: 2018/5/29 17:40
     * @version: 1.0
     */
    @ControllerAdvice(annotations = {RestController.class})
    public class GlobalExceptionHandler {
    	private Logger logger = LoggerFactory.getLogger(this.getClass());
    	/**
    	 * 默认未知异常
    	 * @param request
    	 * @param e
    	 * @return
    	 * @throws Exception
    	 */
    	@ExceptionHandler(value = Exception.class)
    	@ResponseBody
    	public BaseView defaultErrorHandler(HttpServletRequest request, Exception e) throws Exception {
    		BaseView baseView = new BaseView(CodeConstants.SYSTEM_EXCEPTION,CodeConstants.SYSTEM_EXCEPTION_MSG);
    		printLog(request,e,baseView);
    		return baseView;
    	}
    
    	/**
    	 * 参数异常
    	 * @param request
    	 * @param e
    	 * @return
    	 * @throws Exception
    	 */
    	@ExceptionHandler(value = {HttpMessageNotReadableException.class, MissingServletRequestPartException.class ,MissingServletRequestParameterException.class, MultipartException.class})
    	@ResponseBody
    	public BaseView httpMessageNotReadableExceptionErrorHandler(HttpServletRequest request, Exception e) throws Exception {
    		BaseView baseView = new BaseView(CodeConstants.PARAMETER_ERROR,CodeConstants.PARAMETER_ERROR_MSG);
    		printLog(request,e,baseView);
    		return baseView;
    	}
    	/**
    	 * contentType异常
    	 * @param request
    	 * @param e
    	 * @return
    	 * @throws Exception
    	 */
    	@ExceptionHandler(value = {HttpMediaTypeNotSupportedException.class})
    	@ResponseBody
    	public BaseView httpMediaTypeNotSupportedExceptionHandler(HttpServletRequest request, Exception e) throws Exception {
    		BaseView baseView =  new BaseView(CodeConstants.CONTENTTYPE_ERROR,CodeConstants.CONTENTTYPE_ERROR_MSG);
    		printLog(request,e,baseView);
    		return baseView;
    	}
    
    	/**
    	 * 异常信息打印日志
    	 * @param request
    	 * @param e
    	 * @param baseView
    	 */
    	private void printLog(HttpServletRequest request,Exception e,BaseView baseView){
    		logger.error(AppUtil.getExceptionDetail(e));
    		LogEntity logEntity = new LogEntity();
    		logEntity.setHttpMethod(request.getMethod());
    		logEntity.setUrl(request.getRequestURL().toString());
    		logEntity.setIp(request.getRemoteAddr());
    		logEntity.setArgs(AppUtil.getRequestBody(request));
    		logEntity.setLogType(AppConstants.LOG_TYPE_HTTP);
    		logEntity.setReqParams(JsonUtil.objectToJson(request.getParameterMap()));
    		logEntity.setRespParams(JsonUtil.objectToJson(baseView));
    		logger.error(">>>"+JsonUtil.objectToJson(logEntity));
    	}
    
    }
    
  • 相关阅读:
    CSS的margin塌陷
    css white-space
    float的理解
    html标签元素分类
    Sublime text3使用技巧及快捷键
    JSON
    js原生Ajax的封装与使用
    XMLHttpRequest基础知识
    HTTP的一些基础知识
    创建兼容的XHR对象
  • 原文地址:https://www.cnblogs.com/zhucww/p/9319947.html
Copyright © 2011-2022 走看看