一. SpringBoot热部署
我们在做SpringBoot project的时候,如果启动了服务器,又在原来的代码基础上进行了一些修改,我们想要看到修改后的结果必须得重新启动服务器才可以,这时,SpringBoo为了提高开发效率,给我们提供了热部署.
1.添加pom依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
2.重新编译(ctrl+F9,或者手动重新编译也可)
这样我们可以在已经启动服务器的基础上修改业务逻辑代码,然后重新编译一下即可,就不需要停止服务器再运行了,提高了我们的开发效率.
二.java日志体系
1. 日志门面和日志框架
日志门面和日志框架的关系就好比接口(interface)和接口实现类之间的关系,我们是面向接口编程,同时对应了我们面向日志门面编程.日志门面不提供具体的解决方案,只提供接口,具体的解决方案需要使用到具体的日志框架,常见的日志门面和日志框架如下图:
2. 常见日志门面和框架
2.1 JUL
JUL是JDK自己的,所以在使用时可以不需要引入jar包
java
import java.util.logging.Logger;
public class JulTest {
public static void main(String[] args) {
Logger logger = Logger.getLogger(JulTest.class.getName());
String name = "张三";
logger.info(name);
}
}
2.2 log4j
(1) 导入jar包
xml
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
(2) 新建log4j.properties文件(约定大于配置),并且配置
ini
log4j.rootLogger=trace, first
#log4j.rootLogger=trace, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] ===@@@=== %m%n
log4j.appender.first=org.apache.log4j.ConsoleAppender
log4j.appender.first.layout=org.apache.log4j.PatternLayout
log4j.appender.first.layout.ConversionPattern=%d %p [%c] ===log4j=== %m%n
(3) 代码实现日志
arduino
import org.apache.log4j.Logger;
public class Log4jTest {
public static void main(String[] args) {
Logger logger = Logger.getLogger(Log4jTest.class);
// trace<debug<info<warn<error
// logger调用的必须比log4j.properties里面的小才可以输出
logger.info("log4j");
}
}
2.3 log4j2
(1) 导入jar包
xml
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.2</version>
</dependency>
(2)添加log4j2.xml(约定大约配置),并且配置
xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="OFF">
<appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} ===log4j2=== %msg%n"/>
</Console>
</appenders>
<loggers>
<root level="info">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
(3) 代码实现日志
arduino
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4j2Test {
public static void main(String[] args) {
Logger logger = LogManager.getLogger(Log4j2Test.class);
logger.info("Log4j2Test");
}
}
2.4 logback
(1) 导入jar包(因为logback是slf4j下面的日志框架,所以在导入jar包的时候最好把slf4j包也导入进来)
xml
<!-- slf4j日志门面 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<!-- slf4j日志实现-logback日志框架 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
(2) 添加logback.xml配置文件(约定大于配置)
xml
<configuration>
<!--appender 追加器 日志以哪种方式进行输出
name 取个名字
class 不同实现类会输出到不同地方
ch.qos.logback.core.ConsoleAppender 输出到控制台
-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 格式 -->
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{100} ===logback=== %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<!-- 将当前日志级别输出到哪个追加器上面 -->
<appender-ref ref="STDOUT" />
</root>
</configuration>
(3) 代码实现日志
arduino
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jTest {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(Slf4jTest.class);
logger.info("Slf4Test");
}
}
2.5 jcl日志门面
xml
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
jcl日志门面,这个配置了之后会自动寻找,如果有log4j就使用log4j框架,如果没有log4j就使用JDK自带的JUL框架
三 slf4j大一统
我们可以看到,有好几个日志框架可以实现日志功能,我们如果在开发过程中不同的人用不同的日志框架可能会报错,这里就出了一个很好的解决问题--slf4j大一统.
我们可以使用slf4j提供的桥接器,让slf4j日志门面能够和其他的日志框架相兼容,我们在代码中只需要写一个代码接口,到时候想使用那个日志框架直接导入jar包然后添加配置文件即可.
1. slf4j与jcl兼容(其实也是和JUL日志框架相兼容,因为JCL默认先找log4j,如果没有log4就会调用JDK自带的JUL日志框架)
(1) 添加slf4j与jcl桥接器
xml
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jcl</artifactId>
<version>1.7.30</version>
</dependency>
(2) 因为这个是JDK自带的,所以不需要添加配置文件
(3) 使用统一代码
2. slf4j与log4j兼容
(1) 添加slf4j与log4j桥接器
xml
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
(2) 添加log4j.properties配置文件(前文已经提过,这里不再重复)
(3) 使用统一代码
3. slf4j与log4j2兼容
(1) 添加slf4j与log4j2桥接器
xml
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.17.2</version>
</dependency>
(2) 添加log4j2.xml配置文件(前文已经提到,这里不再重复)
(3) 使用统一代码
统一代码:
arduino
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jTest {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(Slf4jTest.class);
logger.info("Slf4Test");
}
}
我这里只是做一个示范,我们可以直接使用这套代码,把slf4jjar包导入进来了然后想使用那个日志框架只需要导入与slf4j的桥接器配置对应文件即可,因为logback是slf4j自己的日志框架,所以在使用logback日志框架只需要导入对应jar包配置文件即可.
四 slf4j实现统一输出
在一个project里面,可能会使用到了很多日志框架,那么怎么在不修改源代码的情况下将他们统一输出呢?答案是使用"适配器"
举个例子: 一个project中模块一使用了log4j,模块二使用了slf4j+log4j2,这是我们为了统一,在不修改源代码的情况下,添加一个log4j和slf4j的适配器,原理就是程序本身要调用log4j的API,但是遇到了log4j和slf4j适配器之后被"拦截",直接调用slf4j日志门面对应框架的API,因为这里假设的是slf4j+log4j2,所以模块一使用日志会直接使用slf4j+log4j2中的API,从而达到了大一统.
1. log4j和slf4j适配器
xml
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.30</version>
</dependency>
切记: 如果使用了log4j适配器,那么要么把适配器放在log4j包的前面,或者把log4j包删除,保证适配器能够拦截到.
2. jcl和slf4j的适配器
xml
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.32</version>
</dependency>
在这里使用了jcl,所以必须把jcl包导入进来,同时,因为jcl性质,会先找到log4j,如果log4j不存在,才会去找jul,再把对应的日志API通过适配器拦截到slf4j里面
五 SpringBoot日志
SpringBoot默认是slf4j+logback日志的,在"场景启动器"里面,不需要我们手动导入,可以直接使用
1. 简单使用日志
如果大家导入了lombok包,在使用slf4j日志的时候可以不需要实例化类,通过@Slf4j注解
2. SpringBoot日志框架改变(slf4j+logback--slf4j+log4j2)
(1) 在场景启动器里面修改logback--log4j2
xml
<!--引入Log4j2的场景启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
这一步是将logback依赖踢出去,然后将log4j2场景启动器导入进来
(2) 添加配置文件(log4j2.xml)
xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="OFF">
<appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} ++++ %msg%n"/>
</Console>
</appenders>
<loggers>
<root level="info">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
3. SpringBoot日志转换(slf4j+logback-->slf4j+log4j)
(1) 排除logback桥接器
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<artifactId>logback-classic</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
</exclusions>
</dependency>
(2) 添加log4j桥接器
xml
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
(3) 添加log4j.properties配置文件(上文已经讲过,不重复)
- 自定义日志配置文件(logback.xml,约定大于配置)
xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="1 seconds">
<contextName>logback</contextName>
<property name="log.path" value="d:/data/applogs/xxl-job/xxl-job-executor-sample-springboot.log"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}.%d{yyyy-MM-dd}.zip</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="console"/>
<appender-ref ref="file"/>
</root>
<logger name="com.cctv" level="debug" />
</configuration>
自定义配置文件讲解:
(1) property用来这只变量,name表示变量名,value表示值
(2) appender有两个属性,name和class,name表示输出在控制台还是以文件的形式,class表示对应的类
(3) appender-ref: 表示输出那种,如果只有一个就输出一个,如果有多个就输出多个.比如图中就有file和console,这个就是说明日志输出在控制台同时生成文件.
(4) root level: 表示root是一个什么样的日志级别(trace<debug<info<warn<error),logger可以通过name和level指定对应模块的日志级别
(5) encoder和pattern用来输出日志具体格式
(6) rollingPolicy日志回滚策略: 图中用了TimeBasedRollingPolicy(基于时间的回滚策略),fileNamePattern是必要节点,我们将日志压缩成一个zip包.