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

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

首先明白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. 返回标签数据示例 (PHP)

    标签接口函数 获取标签数据 array uc_tag_get(string tagname [, array nums]) 函数参数 参数 含义 string tagname 标签名称 array n ...

  2. 如何给echarts图表添加下载图表成图片的功能

    先打开一个现成的图表效果图,注意图中圈出的地方,如图   打开源码找到option,如图   在option下添加toolbox,如图   在toolbox下添加feature,如图   在featu ...

  3. mysql 自动执行事件

    首先配置mysql的配置文件my.ini,  加上event_scheduler = 1  开启自动执行事件配置 demo drop event event_test; CREATE EVENT ev ...

  4. Spring MVC 异常处理 - DefaultHandlerExceptionResolver

    对一些特殊的异常进行处理,比如方法类型不匹配, 转换错误.

  5. python 在内存中处理tar.bz2文件

    如果tar.bz2文件是通过网络进行下载,那么可以直接在内存进行解压后读取文件内容,不用将文件缓存到本地然后解压再进行读取,可以节省IO. 处理经过gzip压缩的tar文件的方法见:https://s ...

  6. spring coud feign

    1. 依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>sprin ...

  7. 解决IE下载 apk/ipa 变成zip:Android 手机应用程序文件下载服务器 配置解决方法

    解决IE apk/ipa变成zip:Android 手机应用程序文件下载服务器 配置解决方法 APK文件其实是zip格式,但后缀名被修改为apk,通过UnZip解压后,可以看到Dex文件,Dex是Da ...

  8. 迷你MVVM框架 avalonjs 学习教程22、avalon性能大揭密

    avalon之所以能在页面处理1W个绑定(angular对应的数字是2000),出于两个重要设计--基于事件驱动的双向绑定链及智能CG回收机制. avalon的双向绑定链是通过Object.defin ...

  9. sessionpage1

    session1 <%@page import="java.text.SimpleDateFormat"%> <%@ page language="ja ...

  10. 吴裕雄 数据挖掘与分析案例实战(14)——Kmeans聚类分析

    # 导入第三方包import pandas as pdimport numpy as np import matplotlib.pyplot as pltfrom sklearn.cluster im ...