日志框架
缘由
如果像我们初学者,想知道代码运行到哪里了,一般都是会System.out.println()
进行输出到控制台查看代码运行的情况,好知道代码错误在哪里
但是大型的系统里面,如果有很多的打印语句嵌套在里面的话,
就是很多多余的无效语句,只是为了让程序员知道而已
后来呢大家就想写一些框架来记录和知道系统运行时的一些运行消息
就变成了日志框架,但是后来需求就越来越多了,如果需要更新系统里面的
日志框架的话,也需要重新修改一下系统的API,这样就很麻烦
所以后来聪明的程序员就写了一个统一的接口层:日志门面
它就是一个日志的抽象层
然后我们系统里面就只需要导入日志的实现就可以了
框架
现在市面上的日志框架
JUL(java.util.logging),
JCL(ApacheCommons Logging),
Log4j,
Log4j2,
Logback、
SLF4j、
jboss-logging
| |
---|
日志门面 | 日志实现 |
JCL(Jakarta Commons Logging) ,SLF4j(Simple Logging Facade for Java) ,jboss-logging | Log4j , JUL(java.util.logging),Log4j2, Logback |
然后左边选一个门面(抽象层)、右边来选一个实现;
日志门面: SLF4J;
日志实现:Logback;
JCL(Jakarta Commons Logging):太老了
jboss-logging :不适合
Log4j:有性能问题,已经升级为Logback
JUL(java.util.logging):java里面的
Log4j2: 阿帕奇模仿Log4j写的
SpringBoot:底层是Spring框架,Spring框架默认是用JCL;
SpringBoot选用 SLF4j和logback;
使用
官网
以后开发的时候,日志记录方法的调用,
不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法;
给系统里面导入slf4j的jar和 logback的实现jar
每一个日志的实现框架都有自己的配置文件。
使用slf4j以后,配置文件还是做成日志实现框架自己本身的配置文件;
比如下面的官网给的图中,
SLF4J是统一的接口层,如果没有导入实现层,就调用的啥都没
如果导入的是logback,就是调用这个的实现类的方法
如果想使用老的log4j层,就需要加入一个适配器layer就行了
适配器一般是实现SLF4J的方法,然后实际调用的是log4j的方法
官网给的例子:
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");
}
}
但是上面只是单个框架进行整合的时候进行切换日志的实现的方法
但是实际上我们在写项目的时候肯定是不止只有一个框架的,而是多个框架进行整合
比如Spring我们导包的时候需要导入commons-logging日志包,
Hibernate需要导入的是jboss-logging日志包
但是这样五花八门的怎么进行统一呢?
官网进行了说明
按照官方的例子,就是比如我们使用的application里面使用的日志门面是
SLF4J,然后下面选用的实现就是logback,从上向下看是没什么问题的,
然后就是我们项目里面的其他的框架了,横着看,里面有个框架使用的是
commons-logging的日志框架,还有使用log4和java.utill.logging的
然后我们想统一使用logback日志框架
就比如上面的commons-logging是spring使用的,如果导入的是spring框架
我们就得必须导入commons-logging的jar包,
如果不导入的话spring在运行时需要日志的地方就会报错
所以就出现了jct-over-slf4j.jar这个适配包,
里面的东西的结构和commons-logging很相似,但是具体的实现却是调用的是SLF4J
里面的抽象方法,所以造成了偷梁换柱的效果
所以总的一句话就是:
让系统中所有的日志都统一到slf4j步骤:
1、将系统中其他日志框架先排除出去;
2、用中间包来替换原有的日志框架;
3、我们导入slf4j其他的实现
SpringBoot的日志选择
里面导入了这个
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
所以SpringBoot底层也是使用slf4j+logback的方式进行日志记录
SpringBoot也把其他的日志都替换成了slf4j;
注意:
如果我们要引入其他框架,一定要把这个框架的默认日志依赖移除掉
SpringBoot能自动适配所有的日志,
而且底层使用slf4j+logback的方式记录日志,引入其他框架的时候,只需要
把这个框架依赖的日志框架排除掉即可;
我们来看看SpringBoot是不是将这些框架的依赖日志框架排除
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
我们还可以看下中间转换包是如何进行转换的
就是下面这样进行偷梁换柱
SpringBoot的日志使用
@org.junit.Test
public void MyLogger()
{
Logger logger = LoggerFactory.getLogger(getClass());
logger.trace("trace级别日志");
logger.debug("debug级别日志");
logger.info("info级别日志");
logger.warn("warn级别日志");
logger.error("error级别日志");
}
#这是修改日志级别,只是修改了jane包下的日志级别的输出
logging.level.jane=trace
#这是指定日志输出的文件
#可以指定完整的绝对路径,如果没有指定路径就在当前项目下生成对应的日志文件
#logging.file=G:/springboot.log
#这是在当前磁盘根路径(c盘)创建spring文件夹和子文件夹log,
#然后创建默认的文件spring.log作为默认文件输出
#logging.path=/spring/log
#logging.file和logging.path两个本来是冲突的,指定其中一个就行了
# 在控制台输出的日志的格式
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
SpringBoot的日志自定义配置
SpringBoot的日志框架的默认配置写在的地方
官网的自定义配置说法
给类路径下放上每个日志框架自己的配置文件即可;SpringBoot就不使用他默认配置的了
就好比如上面说的,我们使用的是Logback框架
然后在类路径里面放一个
logback-spring.xml , logback-spring.groovy , logback.xml or logback.groovy
这些中的其中一个就可以了
然后下面还介绍了一个更加高级点的东西
如果你写的是:logback.xml:直接就被日志框架识别了;
如果你写的是logback-spring.xml:
日志框架就不直接加载日志的配置项,
由SpringBoot解析日志配置,可以使用SpringBoot的高级Profile功能
比如你写的是logback-spring.xml,就可以在里面写:
<springProfile name="dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
</springProfile>
如果你写的是logback.xml还写上面的高级功能,就会报错:no applicable action for [springProfile]
下面是一个例子:logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
<!-- 定义日志的根目录 -->
<property name="LOG_HOME" value="/app/log" />
<!-- 定义日志文件名称 -->
<property name="appName" value="atguigu-springboot"></property>
<!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<!--
日志输出格式:
%d表示日期时间,
%thread表示线程名,
%-5level:级别从左显示5个字符宽度
%logger{50} 表示logger名字最长50个字符,否则按照句点分割。
%msg:日志消息,
%n是换行符
-->
<layout class="ch.qos.logback.classic.PatternLayout">
<springProfile name="dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
</springProfile>
<springProfile name="!dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
</springProfile>
</layout>
</appender>
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
<appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 指定日志文件的名称 -->
<file>${LOG_HOME}/${appName}.log</file>
<!--
当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--
滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动
%i:当文件大小超过maxFileSize时,按照i进行文件滚动
-->
<fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<!--
可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,
那些为了归档而创建的目录也会被删除。
-->
<MaxHistory>365</MaxHistory>
<!--
当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy
-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 日志输出格式: -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
</layout>
</appender>
<!--
logger主要用于存放日志对象,也可以定义日志类型、级别
name:表示匹配的logger类型前缀,也就是包的前半部分
level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
false:表示只用当前logger的appender-ref,true:
表示当前logger的appender-ref和rootLogger的appender-ref都有效
-->
<!-- hibernate logger -->
<logger name="jane" level="debug" />
<!-- Spring framework logger -->
<logger name="org.springframework" level="debug" additivity="false"></logger>
<!--
root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。
-->
<root level="info">
<appender-ref ref="stdout" />
<appender-ref ref="appLogAppender" />
</root>
</configuration>
SpringBoot的切换日志框架
将SLF4J+logback切换成SLF4J+log4j
想按照官网提供的那个图
排除logback‐classic,log4j‐over‐slf4j这些依赖
然后导入slf4j-log4j12依赖
<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+logback切换成SLF4J+log4j2
这个是将spring-boot-starter-logging进行排除
然后将它替换成spring-boot-starter-log4j2
所以后面导入spring-boot-starter-log4j2就行了
因为默认使用的是spring-boot-starter-logging
<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>