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. 图像识别基本算法之SURF

    图像识别.人脸识别可行的算法有很多.但是作为学习,如果能理清这个问题研究的历程及其主线,会对你深入理解当前研究最新的发展有很多帮助.本文是自己在学习过程中的笔记,大多内容来自于网络,出处请参考最后的引 ...

  2. 从锅炉工到AI专家(11)(END)

    语音识别 TensorFlow 1.x中提供了一个语音识别的例子speech_commands,用于识别常用的命令词汇,实现对设备的语音控制.speech_commands是一个很成熟的语音识别原型, ...

  3. 前端笔记之HTML5&CSS3(上)新特性&音频视频&本地存储&自定义属性

    一.HTML5简介 HTML 5 的第一份正式草案已于2008年1月22日公布.HTML5 仍处于完善之中.然而,大部分现代浏览器已经具备了某些 HTML5 支持. 2014年10月29日,万维网联盟 ...

  4. 行为驱动:Cucumber + Selenium + Java(一) - 环境搭建

    1.1 什么是行为驱动测试 说起行为驱动,相信很多人听说过. 行为驱动开发-BDD(Behavior Driven Development)是一个诞生于2003年的软件开发理念.其关键思想在于通过与利 ...

  5. 内核中 xxx_initcall 的调用过程分析

    内核版本:linux-4.19 上一篇文章提到了这段代码: arch_initcall_sync(of_platform_default_populate_init); 它的功能是完成 device_ ...

  6. 第36章 扩展授权 - Identity Server 4 中文文档(v1.0.0)

    OAuth 2.0为令牌端点定义了标准授权类型,例如password,authorization_code和refresh_token.扩展授权是一种添加对非标准令牌颁发方案(如令牌转换,委派或自定义 ...

  7. [android] 创建模拟器遇到的常见错误

    1.错误提示: invalid command line sdk安装目录有中文添加ANDROID_SDK_HOME环境变量,指向sdk安装目录2.模拟器无法安装应用模拟器开启其实是开启了的程序占用这个 ...

  8. PHP 中的Trait

    概述 在PHP中有一种代码复用的技术, 因为单继承的问题, 有些公共方法无法在父类中写出, 而 Trait可以应对这种情况, 它可以定义一些复用的方法, 然后在你需要使用的类中将其引入即可. 刚开始的 ...

  9. 功能强大的PDF实用工具

    PDF实用工具(PDFTool)是北京博信施科技有限有限公司研制开发的一款专门提供对PDF文件进行编辑.加工的处理软件.本软件具有对PDF文件进行分割.结合.加密.解密.添加水印.设定有效期限等多种功 ...

  10. php怎么做网站?如何用PHP开发一个完整的网站?

    1.PHPer应具备的知识 (1)PHP知识: 熟练掌握基础函数,PHP语句(条件.循环),数组(排序.读取),函数(内部 构造),运算(数学 逻辑),面向对象(继承 接口 封装 多态静态属性)等. ...