一、日志

1、简介

市面上有许多的日志框架,比如 JUL( java.util.logging), JCL( Apache Commons Logging), Log4j, Log4j2, Logback、 SLF4j、 jboss-logging等等。

Spring Boot 默认采用了slf4j+logback的形式 ,slf4j是个通用的日志门面,logback就是个具体的日志框架了,我们记录日志的时候采用slf4j的方法去记录日志,底层的实现就是根据引用的不同日志jar去判定了。所以Spring Boot也能自动适配JCL、JUL、Log4J等日志框架,它的内部逻辑就是通过特定的JAR包去适配各个不同的日志框架。

从上图可以看出,Spring Boot通过jul-to-slf4j.jar去适配了JUL日志框架,通过log4j-to-slf4j.jar去适配了log4j日志框架。我们得知道,Spring5.x相对于Spring4.x有个不同的地方就是对底层使用的日志框架有了个大的改变,去除了原来默认使用的JCL 框架,而是采用SLF4j这个通用的日志门面,所以Spring Boot2.x相对于Spring Boot1.x来说去除了对JCL的适配。

SpringBoot能自动适配所有的日志,其底层使用slf4j+logback的方式记录日志,引入其他框架的时候,只需要 把这个框架依赖的日志框架排除掉即可,因为Spring Boot会通过自己的jar去替代;

2、SLF4J使用

1)如何在系统中使用 SLF4J ?

以后在开发中,日志记录方法的调用不应该直接使用日志的实现类,而是调用抽象层里面的方法;

给系统中导入 slf4j 的 jar 包和 Logback 的 jar 包

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {public static void main(String[] args) {Logger logger = LoggerFactory.getLogger(HelloWorld.class);logger.info("Hello World");}
}

2)问题

一个系统的开发不可以只依赖于一个东西,如A系统中使用了(slf4j+Logback) 的日志记录,但是系统还会使用Spring(commons-logging)、Hibernate(jboss-loggin)、Mybatis...

那么怎么统一进行日志记录,即别的框架和我一起使用使用 slf4j 进行日志输出?

如何让系统中所有的日志都统一到slf4j?

  • 将系统中的其他日志框架先排除;

  • 用中间包来替换原有的日志框架;

  • 我们导入slf4j其他实现

3、Springboot日志关系

添加依赖

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId><version>2.0.4.RELEASE</version><scope>compile</scope></dependency>

其底层的依赖关系

总结:

  • Springboot底层也是使用slf4j+Logback进行日志记录

  • Springboot也把其他日志换成了slf4j

  • 中间替换包

@SuppressWarnings("rawtypes")
public abstract class LogFactory {
static String UNSUPPORTED_OPERATION_IN_JCL_OVER_SLF4J =
"http://www.slf4j.org/codes.html#unsupported_operation_in_jcl_over_slf4j";
static LogFactory logFactory = new SLF4JLogFactory();
  • 如果我们要引入其他矿建,一定要把这个框架中的默认日志依赖移除掉

<dependency><groupId>org.springframework</groupId><artifactId>spring‐core</artifactId><exclusions><exclusion><groupId>commons‐logging</groupId><artifactId>commons‐logging</artifactId></exclusion></exclusions>
</dependency>

Springboot能够自动适配所有的日志,而且底层是使用slf4j+logback的方式记录日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除即可

4、日志使用

1)默认配置

Springboot默认帮我们配置好了日志,可以在application.properties中修改Springboot日志的默认设置

//日志记录器Logger logger = LoggerFactory.getLogger(getClass());@Testpublic void contextLoads() {/*** 日志的级别,由地到高,可以调整输出的日志级别,就只会打印这个级别及以上的* Springboot默认使用的是inform级别的*/logger.trace("这是trace跟踪日志。。。。");logger.debug("这是debug调试日志。。。。");logger.info("这是通inform通知日志。。。。");logger.warn("这是warm警告日志。。。。。");logger.error("这是error错误日志。。。。");}
#日志级别
logging.level.com.smart.springboot=trace#不指定路径:在当前目录下生成springboot.log日志
#也可以指定路劲
#logging.file=springboot.log
#在当前磁盘的根目录下创建spring和log文件夹;使用默认的spring.log日志文件
logging.path=/spring/log#在控制台输出的日志格式
logging.pattern.console=%d{yyyy‐MM‐dd} [%thread] %‐5level %logger{50} ‐ %msg%n
#指定在文件中输出的格式
logging.pattern.file=%d{yyyy‐MM‐dd}===[%thread]===%‐5level %logger{50} ==== %msg%n
  • 日志输出格式:

  • %d表示日期时间,

  • %thread表示线程名,

  • %‐5level:级别从左显示5个字符宽度

  • %logger{50} 表示logger名字最长50个字符,否则按照句点分割。

  • %msg:日志消息,

  • %n是换行符

  • ‐‐>     %d{yyyy‐MM‐dd HH:mm:ss.SSS} [%thread] %‐5level %logger{50} ‐ %msg%n

如果想要写日志文件,需要设置属性:logging.filelogging.path。两个属性的组合使用,情况如下

2)指定配置

给类路径下放上每个日志框架自己的配置文件,然后Springboot就不使用自己默认的配置了。

logback.xml:直接就被框架识别了

logback-spring.xml:日志框架就不直接加载日志的配置项,由Springboot解析日志配置,可以使用Springboot的高级Profile功能。

5、SpringBoot 使用Logback日志

(1)日志文件引入

直接把logback-spring.xml配置文件放在src/main/resources文件夹下即可

  • 引入依赖:引入了spring-boot-starter-parent依赖,那么其会自动引入logback的相关依赖
  • 新建logback.xml文件,放在classpath路径下(我们一般把其放在项目中的src/main/resources文件夹,该文件夹也对应classpath路径)即可
  • 在代码中记录日志

logback.xml实例:

<?xml version="1.0" encoding="UTF-8"?><!-- scan="true"开启对配置信息的自动扫描(默认时间为60秒扫描一次) 注:当此文件的配置信息发生变化时,此设置的作用就体现出来了,不需要重启服务 -->
<configuration scan="true"><!-- 通过property标签,来存放key-value数据,便于后面的动态获取,提高程序的灵活性 --><property name="log-dir" value="log" /><property name="log-name" value="logFile" /><!-- >>>>>>>>>>>>>>>>>>>>>>>>>配置appender(可以配置多个)>>>>>>>>>>>>>>>>>>>>>>>>> --><!-- name:自取即可, class:加载指定类(ch.qos.logback.core.ConsoleAppender类会将日志输出到>>>控制台), patter:指定输出的日志格式 --><appender name="consoleAppender"class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}:%L- %msg%n</pattern><!-- 日志编码 --><charset class="java.nio.charset.Charset">UTF-8</charset></encoder></appender><!-- name:自取即可, class:加载指定类(ch.qos.logback.core.rolling.RollingFileAppender类会将日志输出到>>>指定的文件中), patter:指定输出的日志格式 file:指定存放日志的文件(如果无,则自动创建) rollingPolicy:滚动策略>>>每天结束时,都会将该天的日志存为指定的格式的文件 FileNamePattern:文件的全路径名模板 (注:如果最后结尾是gz或者zip等的话,那么会自动打成相应压缩包) --><appender name="fileAppender"class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 把日志文件输出到:项目启动的目录下的log文件夹(无则自动创建)下 --><file>${log-dir}/${log-name}.log</file><!-- 把日志文件输出到:name为logFilePositionDir的property标签指定的位置下 --><!-- <file>${logFilePositionDir}/logFile.log</file> --><!-- 把日志文件输出到:当前磁盘下的log文件夹(无则自动创建)下 --><!-- <file>/log/logFile.log</file> --><rollingPolicyclass="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- TimeBasedRollingPolicy策略会将过时的日志,另存到指定的文件中(无该文件则创建) --><!-- 把因为 过时 或 过大  而拆分后的文件也保存到目启动的目录下的log文件夹下  --><fileNamePattern>${log-dir}/${log-name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern><!-- 设置过时时间(单位:<fileNamePattern>标签中%d里最小的时间单位) --><!-- 系统会删除(分离出去了的)过时了的日志文件 --><!-- 本人这里:保存以最后一次日志为准,往前7天以内的日志文件 --><MaxHistory>7</MaxHistory><!-- 滚动策略可以嵌套; 这里嵌套了一个SizeAndTimeBasedFNATP策略,主要目的是: 在每天都会拆分日志的前提下,当该天的日志大于规定大小时, 也进行拆分并以【%i】进行区分,i从0开始 --><timeBasedFileNamingAndTriggeringPolicyclass="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>5MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy></rollingPolicy><encoder><!-- 日志输出格式 --><pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}:%L- %msg%n</pattern><!-- 日志编码 --><charset class="java.nio.charset.Charset">UTF-8</charset></encoder></appender><!-- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>使用appender>>>>>>>>>>>>>>>>>>>>>>>>>>>>> --><!--指定[哪个包]下使用[哪个appender],并设置 记录到日志文件中的日志的最下级别(低于次级别的日志信息不回输出记录到日志文件中)注:日志级别有: trace|debug|info|warn|error|fatal注:当有多处指定了要记录的日志的最下日志级别时,走优先级最高的,优先级:logback-spring.xml中 > 启动jar包时 > xxx.properties/xxx.yml中--><!--<logger name="com" level="trace">--><logger name="com"><!-- 指定使用哪个appender --><appender-ref ref="fileAppender" /></logger><!--root:logger的根节点,appender-ref:确定使用哪个appender,将日志信息显示在console注:如果不指定配置此项的话,那么SpringBoot启动后,将不会在console打印任何信息--><root><appender-ref ref="consoleAppender" /></root>
</configuration>

注:

  • 最新的日志,都在logFile.log文件中。
  • 虽然限制了单个log文件的大小,但是其并不是严格的,即:一般会在超过界限大小不多后,进行日志文件的拆分。
  • 上述配置有两个“淘汰”,第一个是“非当天的日志,淘汰”;第二个是“超过了指定大小的日志,淘汰”;

(2)代码中使用LogBack实例

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import com.alibaba.fastjson.JSON;
import com.aspire.mapper.JavaAnnotationMapper;
import com.aspire.model.Employee;
import com.aspire.util.ExceptionUtil;@RunWith(SpringRunner.class)
@SpringBootTest(classes = { AbcLogbackDemoApplication.class })
public class AbcLogbackDemoApplicationTests {/** 自动装配 */@AutowiredJavaAnnotationMapper javaAnnotationMapper;/** Logger实例 */static final Logger logger = LoggerFactory.getLogger(AbcLogbackDemoApplicationTests.class);/*** logback测试** @date 2018年7月26日 下午4:12:56*/@Testpublic void logbackTest() {logger.info("进入logbackTest方法了!");try {Employee employee = new Employee("邓某", 24, "男");logger.info("employee对象的相应参数为:" + JSON.toJSONString(employee));javaAnnotationMapper.singleInsertAutoGetKey(employee);Integer id = employee.getId();logger.info("向表中插入employee对象的数据后,自动获取到的主键为:" + id);// System.out.println(1 / 0);} catch (Exception e) {logger.error("出错咯!错误信息:" + e.getMessage(), e.getCause());// 打印出错误堆栈信息e.printStackTrace();}logger.info("SpringBoot使用logback示例。");logger.info("logbackTest方法执行完毕!");}}

6、切换日志框架

可以按照slf4j日志适配图,进行相关切换,slf4j+log4j的方式

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring‐boot‐starter‐web</artifactId><exclusions><exclusion><artifactId>logback‐classic</artifactId><groupId>ch.qos.logback</groupId></exclusion><exclusion><artifactId>log4j‐over‐slf4j</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions>
</dependency>
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j‐log4j12</artifactId>
</dependency>

切换为 slf4j+log4j2

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring‐boot‐starter‐web</artifactId><exclusions><exclusion><artifactId>spring‐boot‐starter‐logging</artifactId><groupId>org.springframework.boot</groupId></exclusion></exclusions>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring‐boot‐starter‐log4j2</artifactId>
</dependency>

二、使用AOP进行统一日志处理请求

(没用过哟)

1、添加依赖

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>

在完成了依赖之后,一般来说不需要再做任何配置。也许在Spring中使用过注解的方式的人会问是否需要在程序的主类中增加 @EnableAspectJAutoProxy 来启用,实际上不需要。

2、AOP配置

下面是关于AOP的默认配置

# AOP
spring.aop.auto=true # Add @EnableAspectJAutoProxy.
# Whether subclass-based (CGLIB) proxies are to be created (true)
# as opposed to standard Java interface-based proxies (false).
spring.aop.proxy-target-class=false 

也就是说spring.aop.auto属性默认是开启的,也就是只需要引入了AOP依赖之后,默认增加了 @EnableAspectJAutoProxy。而当我们需要使用CGLIB来实现AOP的时候,需要配置 spring.aop.proxy-target-class=true,不然默认使用的是java的实现。

3、aop实现日志的切面

  • 使用 @Aspect 注解将一个java类定义为切面

  • 使用 @Pointcut 定义一个切点,可以是一个规则的表达式

  • 根据需要在切入点不同位置的切入内容

    • 使用 @Before 在切入点开始位置切入内容

    • 使用 @After 在切入点结尾处切入内容

    • 使用 @AfterReturning 在切入点return内容之后切入内容(可以用来对返回值做一些加工处理)

    • 使用 @Around 在切入点前后切入内容,并自己控制何时执行切入点自身的内容

    • 使用 @AfterThrowing 用来处理当切入内容部分抛出异常之后的处理逻辑

@Aspect
@Component
public class LogAspect {Logger logger = LoggerFactory.getLogger(getClass());@Pointcut(value = "execution(* com.smart.springboot.controller.*.*(..))")public void log(){}@Before(value = "log()")public void doBefore(JoinPoint joinPoint){//接收请求,记录请求内容ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();//记录下请求的内容logger.info("URL:"+request.getRequestURI());logger.info("HTTP_METHOD:"+request.getMethod());logger.info("IP:"+request.getRemoteAddr());logger.info("CLASS_METHOD:"+joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName());logger.info("ARGS:"+ Arrays.asList(joinPoint.getArgs()));}@AfterReturning(pointcut = "log()",returning = "res")public void doAfterReturning(Object res){logger.info("Response:"+res);}}

4、注意

切点表达式一定要正确,否则不起作用或者报错。

优化:AOP切面同步问题

在切面中,通过 doBefore 和 doAfterReturning 连个独立函数实现了切点头部和返回内容的处理。如果想要得到消耗时间需要在两个函数中独立获取当前时间,然后计算得出。但是直接使用基本类型会出现线程同步问题,因此我们可以引入ThreadLocal 对象进行处理:

@Aspect
@Component
public class LogAspect {Logger logger = LoggerFactory.getLogger(getClass());ThreadLocal<Long> threadLocal = new ThreadLocal<>();@Pointcut(value = "execution(* com.smart.springboot.controller.*.*(..))")public void log(){}@Before(value = "log()")public void doBefore(JoinPoint joinPoint){threadLocal.set(System.currentTimeMillis());//接收请求,记录请求内容ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();//记录下请求的内容logger.info("URL:"+request.getRequestURI());logger.info("HTTP_METHOD:"+request.getMethod());logger.info("IP:"+request.getRemoteAddr());logger.info("CLASS_METHOD:"+joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName());logger.info("ARGS:"+ Arrays.asList(joinPoint.getArgs()));}@AfterReturning(pointcut = "log()",returning = "res")public void doAfterReturning(Object res){logger.info("Response:"+res);logger.info("SPEEDTIME:"+(System.currentTimeMillis()-threadLocal.get()));}

如果有多个切面日志可以设置不同的优先级使用 @Order(i) 来标识优先级。i 的值越小,优先级越高。

假设我们还有一个checkAspect的切面类,我们设置 @Order(10),之前的LogAspect类设置 @Order(5),所以LogAspect类有更高的优先级:执行顺序如下:

  • 在 @Before 中优先执行 @Order(5) 的内容再执行 @Order(10)的内容;

  • 在 @After和@AfterReturning中优先执行 @Order(10)的内容,再执行 @Order(5)的内容

5、总结

  1. 在切入点前的操作,按照 @Order(i) 的优先级由高到低
  2. 在切入点后的操作,按照 @Order(i) 的优先级由低到高

SpringBoot日志相关推荐

  1. Spring Boot与日志 ——日志框架、日志配置||SLF4j使用||SpringBoot日志关系||切换日志框架

    1.日志框架 SLF4j使用 1.如何在系统中使用SLF4j 以后开发的时候,日志记录方法的调用,不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法: 给系统里面导入slf4j的jar和 lo ...

  2. springboot日志配置

    SpringBoot日志使用 1. 在springboot中测试打印日志 package com.leon.springboot_log;import org.apache.logging.log4j ...

  3. springboot 日志设计结构

    SpringBoot中的日志使用 springboot框架在企业中的使用越来越普遍,springboot日志也是开发中常用的日志系统.springboot默认就是使用SLF4J作为日志门面,logba ...

  4. springboot 日志_Springboot与日志

    1.日志框架 JUL.JCL.Jboss-logging.logback.log4j.log4j2.slf4j.... 日志门面(日志的抽象层) 日志实现 JCL(Jakarta Commons Lo ...

  5. SpringBoot - 日志选择与实现

    SpringBoot - 日志选择与实现 [1]常见的日志门面与实现框架 日志门面 实现框架 JCL(Jakarta Commons logging),SLF4J,Jboss logging Log4 ...

  6. SpringBoot 日志配置

    SpringBoot 日志的配置 如果不配置日志,默认配置是 base.xml 配置日志 在 resource 目录下新建 logback.xml <?xml version="1.0 ...

  7. ELK日志分析系统搭建以及springboot日志发送到ELK中

    前言 安装之前服务器必须装了Java环境,我们这里安装的是7.7.0版本,而且7.7.0版本还必须要求jdk11以上.,最好跟我安装的路径保持一致/usr/local/elk,千万不要在root 安装 ...

  8. springboot日志配置logback-spring.xml

    springboot日志配置logback-spring.xml 日志配置文件logback-spring.xml 放在项目src/main/resources目录下 <?xml version ...

  9. SpringBoot日志级别设置

    SpringBoot日志级别设置 在application.properties配置文件中设置: #设置时区: spring.jackson.date-format=yyyy-MM-dd HH:mm: ...

  10. SLF4j的介绍与使用+SpringBoot日志配置

    关于日志 日志级别 error > warn > info > debug > trace > fatal trace:级别最低 debug:需要调试时候的关键信息 in ...

最新文章

  1. 将二叉树中每一层的节点串成链表
  2. 指尖检测的几种新方法
  3. Go gin内嵌静态资源go-bindata的安装及使用(GVA)
  4. BugKuCTF WEB web3
  5. Maven 学习 (0) Maven 简介
  6. 6、mysql中字段
  7. 织梦 tags.php静态化,dedecms网站tag标签全部静态化的解决方法
  8. 在你的 Linux 桌面嵌入终端窗口
  9. HTML之表单的基本知识
  10. workerman--测试
  11. Spring4-@PostConstruct和@PreDestroy注解的使用
  12. lvs的dr和nat模式配置备忘
  13. 国外计算机应用基础,计算机应用基础试题(国外英文资料).doc
  14. Xmanager Xbrowser--Win10远程连接CentOS7
  15. Jetson TK1
  16. Python+Vue计算机毕业设计H5的豫宛旅游网站v6giy(源码+程序+LW+部署)
  17. 公众账号迁移:微信订阅号怎么升级服务号?
  18. 右键新建缺少word、excel选项问题处理
  19. win10 无线鼠标卡顿,跳帧
  20. 【AI简报20210611期】Arm v9架构来啦、开源实战教你实现文字识别!

热门文章

  1. w ndows摄像头驱动怎么安,电脑中如何安装摄像头驱动
  2. python 发音-python读音
  3. UFS学习一:UTP层和UPIU
  4. 带手续费买卖股票的最大利益[找DP的状态定义到底缺什么?]
  5. arctanx麦克劳林公式推导过程_【数学】「专题」初识泰勒级数(Taylor Series)与泰勒公式(Taylor#x27;s Formula)...
  6. 前端必知必会(一):vue3+node实现网站支付功能
  7. 三星android 截图,三星手机如何截图?几种三星手机截屏的方法教程
  8. uva 11538 Chess Queen
  9. KUKA库卡机器人零点失效维修案例
  10. 前端后台常见问题总结