一、日志门面与日志实现
日志门面:即一个统一的接口层(日志的一个抽象层);
日志实现:即项目中具体使用的哪个日志框架。
下图是目前常见和常用的日志门面和实现。
而SpringBoot选择的是:SLF4j和Logback;
需要说明一下,JCL最后的一次更新是在2014年了,jboss-logging适用的场景过少。事实上SLF4j和Log4j,Logback都是由同一个人开发出来的,而Log4j2事实上仅仅是为了蹭Log4j的热度(这是我自己瞎想的),是由apache公司开发的一个日志框架,目前尚未得到很好的兼容。而Logback是Log4j的升级版本,性能更加的优越,因此springboot最终选择的是SLF4j和Logback。
注:以上观点属于个人观点,不接受反驳和喷子!
二、在SpringBoot项目中使用SLF4j
注:在真实开发项目中,我们一般都是直接调用的是接口,即抽象层的方法,再由接口去调用其实现类的方法。
SLF4j的官方文档:https://www.slf4j.org/
此时项目中以有SLF4j和Logback的依赖:
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-core -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
然而事实上
官方示例一:
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");
}
}
官方示例二:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Wombat {
final Logger logger = LoggerFactory.getLogger(Wombat.class);
Integer t;
Integer oldT;
public void setTemperature(Integer temperature) {
oldT = t;
t = temperature;
logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);
if(temperature.intValue() > 50) {
logger.info("Temperature has risen above 50 degrees.");
}
}
}
由于每一个日志实现的框架都有自己的配置文件,因此即便使用了slf4j接口层,但配置文件还是日志实现的配置文件。
三、SLF4j对其他日志框架的兼容
SpringBoot选择的是SLF4j和Logback,已经是很好的日志框架了,为什么还需要兼容其他的日志框架呢?原因在于,往往一个springboot项目同时也会依赖其他的框架,例如spring使用的是JCL,hiberbate使用的日志框架是jsboss-logging等等;如果不进行统一的日志管理,那么整个项目的日志就会显得杂乱,甚至会出现迷之错误。幸好这一点springboot已经为我们考虑到了,并作出了很好的解决方案。
即如下图所示(来源于官方文档),在这之间加一个适配层。意思就是当这个项目需要用到JCL(Jakarta Commons Logging)
日志框架的时候,在maven导入依赖时却并不导入JCL(Jakarta Commons Logging)的相关依赖,而是导入如图所示的替换依赖jar包(jcl-over-slf4j.jar),此时jcl-over-slf4j.jar会将JCL替换掉。其他的以此类推。
事实上以springboot的强大,我们根本不必要如此麻烦,只需要引入一个依赖,而以上的jar也就被依赖进来了,代码如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
创建项目的时候仅需要选择web模块就行了
在pom文件中右键选中Diagrams-->如图所示,则可以看到jar包的思维导图模式
而我们会发现在spring-boot-starter-web下依赖了spring-boot-starter,而spring-boot-starter下又依赖了spring-boot-starter-logging,spring-boot-starter-logging就时我们需要的日志依赖,spring-boot-starter-logging下包含几乎我们所常用的日志依赖,关系图如下:
可以看到实际上springboot已经帮我们做好了关于非Logback日志框架的替换jar包。因此当我们需要使用其他框架时,则必须将这个框架的默认日志依赖排除掉。
总结一句话就是springboot已经能很好的适配所有日志框架,底层使用的时slf4j+logback的方式记录日志,只需要在引入其他框架的时候将这个框架的日志依赖排除掉即可,无需做更多的操作!
排除方式:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<!--需要排除的某些jar包依赖-->
<exclusions>
<!--将spring框架的默认日志依赖排除掉-->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
四、在SpringBoot中关于SLF4j的配置
最简单的快速配置的slf4j日志框架
1.新建一个spring项目时:
2.下载lombok插件,这个插件的用处不仅仅局限于日志方面
3.创建好项目后(若是第一次下载lombok插件,则会要求重启idea),pom.xml文件是以下这样的:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
在主配置文件application.properties中的相关配置:
server.port=8081
#打印的日志级别,优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。
logging.level.root:info
# 在当前磁盘的根路径下创建spring文件夹和里面的log文件夹;使用 spring.log 作为默认文件
#logging.path=/spring/log
# 可以指定完整的路径,如下:
logging.file=E:/logs/springboot.log
# 在控制台输出的日志的格式
logging.pattern.console=%d{yyyy-MM-dd hh:mm:ss} [%thread] %-5level %logger{50} - %msg%n
# 指定文件中日志输出的格式
logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n
controller中的日志打印:
使用的是log.info(...),log.error(XXX),log.debug(~~~~) and so on ....来表示不同级别时的输出打印。
因为使用了lombok插件,只需加入注解@Slf4j,
就可以直接使用log对象,而不需要再使用final Logger logger = LoggerFactory.getLogger(LogController.class)来创建一个日志对象。
package com.yangli.studyspringboot_log.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
public class LogController {
@RequestMapping("/getLog")
public String getLog(){
log.info("=========此处是info级别日志的打印!==========");
log.error("--------此处是error级别日志的打印!---------");
log.debug("········此处是debug级别日志的打印!·········");
return "打印日志";
}
}
详解:
logging.level.*:
可以用来配置具体输出哪些包的日志级别,root表示当前项目。
例如:
logging.level.root=INFO
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate=ERROR
logging.path:
- 只配置文件名,不指定具体盘符及其目录时,会将日志文件保存在当前项目中,springboot会自动创建一个日志目录在项目的子目录中;
logging.path=springboot.log
- 指定日志文件存放的具体路径(可带盘符,windows系统中若不带盘符则会在项目所在磁盘的根路径下创建相应的文件夹和日志文件),日志文件的名称需要通过logging.file配置,若logging.file中也指定了日志文件的路径则以logging.file为准,若没有配置logging.file,则日志文件的名称采用SpringBoot默认的spring.log
logging.file:
- 只配置文件名,不指定具体盘符及其目录时,会将日志文件保存在当前项目中
- 指定具体盘符及其目录时,会将日志文件保存在指定目录下的该文件中,尾缀为.log的就是日志文件。若没有配置文件名,则以默认的spring.log为日志文件名。
注:logging.file的优先级高于logging.path,即二者同时存在时,以logging.file为准。若是此二者皆没有配置,则springboot的日志仅会在控制台打印出来。
logging.pattern.console和logging.pattern.file:
前者表示在控制台打印的格式,后者为在日志文件中的保存格式。
日志格式:
%d——日期时间
%thread——线程名
%‐5level——级别(从左显示5个字符宽度)
%logger{50}——logger名字(类的全类名),最长50个字符,否则按照句点分割
%msg——日志消息
%n——换行符
示例:
# 在控制台输出的日志的格式
logging.pattern.console=%d{yyyy-MM-dd hh:mm:ss} [%thread] %-5level %logger{50} - %msg%n
# 指定文件中日志输出的格式
logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n
结果:
控制台:
日志文件: