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

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

  1. SpringBoot系列——自定义统一异常处理

    前言 springboot内置的/error错误页面并不一定适用我们的项目,这时候就需要进行自定义统一异常处理,本文记录springboot进行自定义统一异常处理. 1.使用@ControllerAd ...

  2. 关于spring 事务 和 AOP 管理事务和打印日志问题

    关于spring 事务 和 AOP 管理事务和打印日志问题 1. 就是支持事务注解的(@Transactional) . ​ 可以在server层总使用@Transactional,进行方法内的事务管 ...

  3. springboot 整合logback(有全套的日志配置文件)

    logback介绍:基于Log4j基础上大量改良,不能单独使用,推荐配合日志框架SLF4J来使用,可以和springboot很好的整合,也是springboot默认推荐的. 1.在resources ...

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

    在Spring Boot当中,默认使用logback进行log操作.logback支持将日志数据通过提供IP地址.端口号,以Socket的方式远程发送.在Spring Boot中,通常使用logbac ...

  5. SpringBoot(七)_统一异常处理

    我感觉看了这节课,给我的思考还是很多的,感觉受益良多.废话不多说,一起学习. 统一的 外层结构返回 这样利于代码看着也规范,前端处理也统一 # 错误返回 { "code": 1, ...

  6. SpringBoot | 第二十四章:日志管理之AOP统一日志

    前言 上一章节,介绍了目前开发中常见的log4j2及logback日志框架的整合知识.在很多时候,我们在开发一个系统时,不管出于何种考虑,比如是审计要求,或者防抵赖,还是保留操作痕迹的角度,一般都会有 ...

  7. Spring Boot 2.0 教程 | AOP 切面统一打印请求日志

    欢迎关注微信公众号: 小哈学Java 文章首发于个人网站 https://www.exception.site/springboot/spring-boot-aop-web-request 本节中,您 ...

  8. SpringBoot整合Logback日志框架配置全解析

    目录 本篇要点 一.Logback日志框架介绍 二.SpringBoot与Logback 1.默认日志格式 2.控制台输出 3.文件输出 4.日志级别 5.日志组 6.自定义log配置 三.logba ...

  9. SpringBoot统一异常处理后TX-LCN分布式事务无法捕获异常进行回滚

    通常我们使用SpringBoot都会进行统一异常处理,例如写一个BaseController,在BaseController里进行统一异常处理,然后其他的Controller都继承BaseContro ...

随机推荐

  1. SpringBoot入门教程(十七)@Service、@Controller、@Repository、@Component

    spring 2.5 中除了提供 @Component 注释外,还定义了几个拥有特殊语义的注释,它们分别是:@Repository.@Service 和 @Controller.在目前的 Spring ...

  2. SQL Server查询所有的表名、字段名、注释

    SELECT 表名 then d.name else '' end, 表说明 then isnull(f.value,'') else '' end, 字段序号=a.colorder, 字段名=a.n ...

  3. Nacos 发布 v0.8.0 Pre-GA版本,安全稳定上生产?

    服务注册和服务配置开源项目 Nacos 本周发布了 v0.8.0 Pre-GA 版本,作为开源项目生命周期中的里程碑版本之一,v0.8.0 Pre-GA版本支持登录.命名空间.Metrics监控(对接 ...

  4. 【Go】string 优化误区及建议

    原文链接: https://blog.thinkeridea.com/201902/go/string_ye_shi_yin_yong_lei_xing.html 本文原标题为 <string ...

  5. arcgis 10.0中的server报错说工作站服务没有打开

    大家好! 写这篇文章其实我也不知道该不该写,感觉问题其实也不是自己解决的,但是这个问题困恼了我2天,我还将arcgis10.0重装了一次. 下面也不多说了,主要是由于公司的需求,将自己的arcgis1 ...

  6. Nunit的尝试

    (ps:没有代码,只有理论) 单元测试 单元测试(Unit Test)的一个测试用例(Test Case)是一小段代码,用于测试一个小的程序功能的行为是否正常,保证开发的功能子项能正确完成并实现其基本 ...

  7. C# 添加、修改以及删除Excel迷你图表的方法

    Excel表格中的迷你图表能够直观地向我们展示出数据的变化趋势.本文将介绍C#如何实现为表格数据生成迷你图表,以及修改和删除迷你图表的方法.下面将详细讲述. 所用组件工具:Spire.XLS for ...

  8. Java开发笔记(五十九)Java8之后的扩展接口

    前面介绍了接口的基本用法,有心的朋友可能注意到这么一句话“在Java8以前,接口内部的所有方法都必须是抽象方法”,如此说来,在Java8之后,接口的内部方法也可能不是抽象方法了吗?之所以Java8对接 ...

  9. Java开发笔记(七十二)Java8新增的流式处理

    通过前面几篇文章的学习,大家应能掌握几种容器类型的常见用法,对于简单的增删改和遍历操作,各容器实例都提供了相应的处理方法,对于实际开发中频繁使用的清单List,还能利用Arrays工具的asList方 ...

  10. 结合JDK源码看设计模式——简单工厂、工厂方法、抽象工厂

    三种工厂模式的详解: 简单工厂模式: 适用场景:工厂类负责创建的对象较少,客户端只关心传入工厂类的参数,对于如何创建对象的逻辑不关心 缺点:如果要新加产品,就需要修改工厂类的判断逻辑,违背软件设计中的 ...