在开发以及调试过程中,程序员对日志的需求是非常大的,出了什么问题,都要通过日志去进行排查,但是如果日志不清或者杂乱无章,则不利于维护

这边就比较详细的列举几种类型的日志,供大家参考

首先明白logback日志是Spring Boot自带的,不需要引入额外的包

            <dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>

点进pom里的核心依赖,就能看见上面几个,是由Spring Boot自动依赖配置好的,我们只要直接使用就好了

比较简单的是直接在application的配置文件里 写参数配置就行了,他提供了日志级别,日志输出路径等,也能满足基本的日志输出

我们这通过xml文件进行配置 logback-spring.xml

这样就能直接引用到xml了,但是为什么能引用到了

就是在logback里有个默认的机制,内部会有几种标准的文件格式,在LogbackLoggingSystem里标注了

@Override
protected String[] getStandardConfigLocations() {
return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy",
"logback.xml" };
}

所以最为标准的为这里面的四种文件格式,但是如果项目中没有,他还提供了扩展文件格式 就是在后面拼上-spring,例如logback.xml 扩展为logback-spring.xml

ok

下面看下xml里面的内容:

<?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> <!-- 自己打印的日志文件,用于记录重要日志信息 -->
<appender name="MY_INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/category-server-myinfo-log.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>15</MaxHistory>
<!--日志文件最大的大小-->
<MaxFileSize>10MB</MaxFileSize>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<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>
<charset>UTF-8</charset>
</encoder>
</appender>
<logger name="my_info" additivity="true">
<appender-ref ref="MY_INFO_FILE"/>
</logger>
<!--myibatis log configure-->
<logger name="com.example.demo" 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>

这里一共有四块内容,第一是console的日志输出,第二是系统运行日志,第三是警告以上的日志输出(基本上是程序出错日志),第四种是自定义日志

每一块日志由一个appender标签引入

CONSOLE是控制台日志输出,只要规定个格式就行了

FILE是系统运行日志,系统的所有运行信息都会保留,正常我们会把这部分信息保存在硬盘日志文件中,按天按文件大小保存,因为这个内容实在是比较多

ERROR_FILE是WARN级别以上的日志,这块是开发人员和运维人员最多关注的,因为基本上所有的bug都会在这个里面体现

MY_INFO_FILE是自定义日志,想定义自己的日志文件,记录一些重要的信息

这里的日志都是以文件的形式保存在本地,当然像WARN级别以上日志可以异步保存到数据库

日志文件定义好后,接下来就要开始定义业务逻辑了

在针对一些异常日志,我们想尽可能完整准确的抛出异常,一眼就能知道是什么问题,这里我们就需要自定义异常,最多的就是像空指针,数组越界等常见异常

定义基础异常类BaseException继承他的父类RuntimeException

public class BaseException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    public BaseException() {
super();
// TODO Auto-generated constructor stub
} public BaseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
} public BaseException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
} public BaseException(String message) {
super(message);
// TODO Auto-generated constructor stub
} public BaseException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
} }

然后全局异常处理类:GlobalExceptionHandler

@CrossOrigin
@RestControllerAdvice
public class GlobalExceptionHandler{ private static Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class); private static final String APPLICATION_JSON = "application/json";
private static final String UTF_8 = "UTF-8"; /**
* BaseException 处理类
* @Title: HandleBaseException
* @Description: TODO
* @param @param e
* @param @return
* @return ResponseMsg
* @throws
*/
@ExceptionHandler(BaseException.class)
@ResponseBody
public ResponseMsg HandleBaseException(RuntimeException e){
//只能输出捕获到的异常,未捕获到的异常不输出到日志,或者通过aop拦截器拦截所有方法
LOGGER.error(getExceptionDetail(e));
//返回失败信息
Route route = new Route();
ResponseMsg responseMsg = new ResponseMsg(route,ReturnMsgEnum.INTERNAL_ERROR.getCode(),
ReturnMsgEnum.INTERNAL_ERROR.getMsg(), "");
return responseMsg;
} @ExceptionHandler(GlobalException.class)
@ResponseBody
public ResponseMsg HandleGlobalException(Exception e){
//只能输出捕获到的异常,未捕获到的异常不输出到日志,或者通过aop拦截器拦截所有方法
LOGGER.error(getExceptionDetail(e));
//返回失败信息
Route route = new Route();
ResponseMsg responseMsg = new ResponseMsg(route,ReturnMsgEnum.INTERNAL_ERROR.getCode(),
ReturnMsgEnum.INTERNAL_ERROR.getMsg(), "系统未捕获该异常");
return responseMsg;
} public String getExceptionDetail(Exception e) {
StringBuffer stringBuffer = new StringBuffer(e.toString() + "\n");
StackTraceElement[] messages = e.getStackTrace();
int length = messages.length;
for (int i = 0; i < length; i++) {
stringBuffer.append("\t"+messages[i].toString()+"\n");
}
return stringBuffer.toString();
} }
@RestControllerAdvice:表明他是一个Controller 并且是异常拦截的统一处理类
定义针对自定义异常的处理方法:用@ExceptionHandler(BaseException.class)注解标注
BaseException就是刚才的自定义异常
之后所有抛出的BaseException都会由他处理 自定义异常我们都能轻松捕获到了,并且输出到日志里了 如果有些异常我们没有捕获到,我们就可以定义一个切面,让所有方法都经过这个切面处理
/**
* 处理未捕获到的异常
* @ClassName: SpringAOP
* @author Mr.Chengjq
* @date 2018年10月17日
* @Description: TODO
*/
@Aspect
@Configuration
public class SpringAOP {
private static final Logger logger = LoggerFactory.getLogger(SpringAOP.class); /**
* 定义切点Pointcut
* 第一个*号:表示返回类型, *号表示所有的类型
* 第二个*号:表示类名,*号表示所有的类
* 第三个*号:表示方法名,*号表示所有的方法
* 后面括弧里面表示方法的参数,两个句点表示任何参数
*/
@Pointcut("execution(* com.example.demo..*.*(..))")
public void executionService() { } /**
* 方法调用之前调用
* @param joinPoint
*/
@Before(value = "executionService()")
public void doBefore(JoinPoint joinPoint){ //添加日志打印
String requestId = String.valueOf(UUID.randomUUID());
MDC.put("requestId",requestId);
logger.info("=====>@Before:请求参数为:{}",Arrays.toString(joinPoint.getArgs())); } /**
* 方法之后调用
* @param joinPoint
* @param returnValue 方法返回值
*/
@AfterReturning(pointcut = "executionService()",returning="returnValue")
public void doAfterReturning(JoinPoint joinPoint,Object returnValue){ logger.info("=====>@AfterReturning:响应参数为:{}",returnValue);
// 处理完请求,返回内容
MDC.clear();
} /**
* 统计方法执行耗时Around环绕通知
* @param joinPoint
* @return
*/
@Around("executionService()")
public Object timeAround(ProceedingJoinPoint joinPoint) throws Throwable{ //获取开始执行的时间
long startTime = System.currentTimeMillis(); // 定义返回对象、得到方法需要的参数
Object obj = null;
//Object[] args = joinPoint.getArgs();
try {
obj = joinPoint.proceed();
} catch (Throwable e) {
// TODO: handle exception
logger.error(getExceptionDetail(e));
throw new GlobalException();
} // 获取执行结束的时间
long endTime = System.currentTimeMillis();
//MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
// 打印耗时的信息
logger.info("=====>处理本次请求共耗时:{} ms",endTime-startTime);
return obj;
} public String getExceptionDetail(Throwable e) {
StringBuffer stringBuffer = new StringBuffer(e.toString() + "\n");
StackTraceElement[] messages = e.getStackTrace();
int length = messages.length;
for (int i = 0; i < length; i++) {
stringBuffer.append("\t"+messages[i].toString()+"\n");
}
return stringBuffer.toString();
} }
这个切面里未捕获到的异常也全部做特定处理

Spring Boot与Logback的运用(自定义异常+AOP)的更多相关文章

  1. spring boot 中logback多环境配置

    spring boot 配置logback spring boot自带了log打印功能,使用的是Commons logging 具体可以参考spring boot log 因此,我们只需要在resou ...

  2. spring boot 使用logback日志系统的详细说明

    springboot按照profile进行打印日志 log4j logback slf4j区别? 首先谈到日志,我们可能听过log4j logback slf4j这三个名词,那么它们之间的关系是怎么样 ...

  3. Spring Boot 乐观锁加锁失败 - 使用AOP恢复错误

    之前写了一些辅助工作相关的Spring Boot怎么使用AOP.这里继续正题,怎么减少Spring Boot 乐观锁加锁报错的情况(基本可以解决). 1. 包依赖 spring-boot-starte ...

  4. Spring Boot 乐观锁加锁失败 - 集成AOP

    Spring Boot with AOP 手头上的项目使用了Spring Boot, 在高并发的情况下,经常出现乐观锁加锁失败的情况(OptimisticLockingFailureException ...

  5. 剑指架构师系列-spring boot的logback日志记录

    Spring Boot集成了Logback日志系统. Logback的核心对象主要有3个:Logger.Appender.Layout 1.Logback Logger:日志的记录器 主要用于存放日志 ...

  6. 如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志 | 修订版

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...

  7. Spring Boot之logback日志最佳实践

    一.Spring Boot日志介绍 Spring Boot对所有内部日志记录使用了Commons Logging,但是底层日志实现是开放的.为Java Util日志记录.Log4J2和Logback提 ...

  8. 基于Spring Boot的Logback日志轮转配置

    在生产环境下,日志是最好的问题调试和跟踪方法,因此日志的地位是十分重要的.我们平时经常使用的log4j,slf4j,logback等等,他们的配置上大同小异.这里就结合Spring Boot配置一下L ...

  9. spring boot(13)-logback和access日志

    logback logback出自log4j的作者,性能和功能相比log4j作出了一些改进,而配置方法和log4j类似,是spring boot的默认日志组件.在application.propert ...

随机推荐

  1. 0_Simple__UnifiedMemoryStreams

    使用 OpenMP 和 pthreads 两种环境,利用实现统一内存编址,计算基本的矩阵乘法 result = α * A * x + β * result . ▶ 源代码 #include < ...

  2. 11 并发编程-(线程)-信号量&Event&定时器

    1.信号量(本质也是一把锁)Semaphore模块 信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行, 信号量同一时间可以有5个任务拿到锁去执行, 如果说互斥锁是合租 ...

  3. JetBrains产品永久破解

    吃水不忘挖井人,本博客转自:https://www.cnblogs.com/jyiqing/p/7699649.html 目的: 本人使用idea和webstorm进行开发,无奈正版实在是太贵了,只能 ...

  4. mysql 1292-Truncated incorrect double value

    sql = "select id from company where date_year_month = %s" % "2017-3" 出错 将%s改为'%s ...

  5. mysql 2003: Can't connect to MySQL server on '127.0.0.1:3306' (99)

    连接断开的频率太高导致报错,可以在每次连接之间sleep,或者保持一个长连接. ref:https://stackoverflow.com/questions/24884438/2003-cant-c ...

  6. 吴裕雄 python 数据处理(1)

    import time print(time.time())print(time.localtime())print(time.strftime('%Y-%m-%d %X',time.localtim ...

  7. Python内置类型性能分析

    Python内置类型性能分析 timeit模块 timeit模块可以用来测试一小段Python代码的执行速度. class timeit.Timer(stmt='pass', setup='pass' ...

  8. drupal sql 源码解析query.inc 文件

    query.inc 文件: sql语句: $this->condition($field);1707 line public function condition($field, $value ...

  9. debug-stripped.ap_' specified for property 'resourceFile' does not exist.(转载)

    1.错误描述 更新Android Studio到2.0版本后,出现了编译失败的问题,我clean project然后重新编译还是出现抑郁的问题,问题具体描述如下所示: Error:A problem ...

  10. new operator

    [new operator] When the code new Foo(...) is executed, the following things happen: A new object is ...