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));
    }

}

原文地址:https://www.cnblogs.com/zhucww/p/9319947.html

时间: 2024-11-05 12:05:39

springboot aop + logback + 统一异常处理 打印日志的相关文章

springboot整合logback集成elk实现日志的汇总、分析、统计和检索功能

在Spring Boot当中,默认使用logback进行log操作.logback支持将日志数据通过提供IP地址.端口号,以Socket的方式远程发送.在Spring Boot中,通常使用logback-spring.xml来进行logback配置. 首先.创建一个elk的springboot项目,然后先对logback进行配置,配置各项的详细说明可以去看http://aub.iteye.com/blog/1101222,说的很详细.也多参考一下别人关于日志的描述https://www.cnbl

springboot中的统一异常处理

请查看@ControllerAdvice.@ExceptionHandler和@RestControllerAdvice的注释 https://www.cnblogs.com/goloving/p/9142222.html https://yq.aliyun.com/articles/647428 原文地址:https://www.cnblogs.com/suntp/p/11265267.html

springboot + shiro 权限注解、请求乱码解决、统一异常处理

springboot + shiro 权限注解.请求乱码解决.统一异常处理 前篇 后台权限管理系统 相关: spring boot + mybatis + layui + shiro后台权限管理系统 springboot + shiro之登录人数限制.登录判断重定向.session时间设置 springboot + shiro 动态更新用户信息 基于前篇,新增功能: 新增shiro权限注解: 请求乱码问题解决: 统一异常处理. 源码已集成到项目中: github源码: https://githu

aop打印日志

首先在资源目录下创建logback.xml文件 文件的内容 <?xml version="1.0" encoding="UTF-8"?> <configuration> <!--======================================= 本地变量 ======================================== --> <!--在没有定义${LOG_HOME}系统变量的时候,可以设置此本地

springboot整合mybatis将sql打印到日志(转)

在前台请求数据的时候,sql语句一直都是打印到控制台的,有一个想法就是想让它打印到日志里,该如何做呢? 见下面的mybatis配置文件: [html] view plain copy print? <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "htt

Spring MVC 中使用AOP 进行统一日志管理--XML配置实现

1.介绍 上一篇博客写了使用AOP进行统一日志管理的注解版实现,今天写一下使用XML配置实现版本,与上篇不同的是上次我们记录的Controller层日志,这次我们记录的是Service层的日志.使用的工程还是原来的那个,具体的Spring mvc 工程搭建暂不介绍.上篇记录controller层日志的时候是将切面类组件叫给spring MVC 进行管理,因为 controller 也是交给spring MVC进行管理的,但是记录service 层日志的时候应该就应该再spring 容器中进行了,

springboot配置logback日志

springboot配置logback日志 java web 下有好几种日志框架,比如:logback,log4j,log4j2(slj4f 并不是一种日志框架,它相当于定义了规范,实现了这个规范的日志框架就能够用 slj4f 调用).本篇主要说明spring boot配置logback springboot配置日志框架之前,要先取消springboot默认的日志框架 一.在pom.xml中加入取消默认日志框架 <dependency> <groupId>org.jxls</

Springboot:logback日志管理(九)

Springboot默认使用的日志框架就是logback 创建自定义的logback-spring.xml放在resources类目录下即可 logback-spring.xml: <?xml version="1.0" encoding="UTF-8"?> <!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不

Spring MVC自定义统一异常处理类,并且在控制台中输出错误日志

在使用SimpleMappingExceptionResolver实现统一异常处理后(参考Spring MVC的异常统一处理方法), 发现出现异常时,log4j无法在控制台输出错误日志.因此需要自定义一个继承至SimpleMappingExceptionResolver的 RrtongMappingExceptionResolver类,在RrtongMappingExceptionResolver中通过 log.error(ex.getMessage())的方式输出日志到控制台上.以下是具体的配