目录
常见的日志门面
JCL,slf4j(Simple Logging For java)
常见的日志实现
JUL,log4j,logback,log4j2
日志框架介绍
JUL
Java自带的日志框架,不需要引入,直接可以使用
Log4j
非常经典的日志框架,但是已经停止更新和维护了
LogBack
logBack和log4j不是同一个公司开发的,LogBack是Spring自动集成的日志框架了。目前比较流行
Log4j2
Log4j2是Log4j的进阶版,相比于LogBack,在异步的时候速度可以快近10倍,目前Spring项目中,都会把自动引入的LogBack注释掉,改用Log4j2. 未来使用Log4j2+slf4j这套组合一定是未来的几年的主流日志框架。
SLF4J的使用
简介
SLF4J(Simple Logging Facade For Java)是给Java日志访问提供的一套标准,具体的实现,主要交给log4j,logback,log4j2等日志框架。SLF4J其实也实现了简单的日志实现,但是几乎不会有人去使用,而日志框架一般会选择slf4j-api作为门面,配上具体的实现框架,中间使用连接桥(适配器)来完成日志框架的建立。 slf4j-api 和 slf4j-simple 引入。 api是日志门户,simple是日志的简单实现。
slf4j-api 和 slf4j-simple 使用
log的级别有trace,debug,info,warn,error和fatal六个级别,默认情况下,只会输出info,warn和error,fatal四个级别的日志。 占位符的使用,可以使用{}作为占位符,后边跟具体的变量,只要能使用占位符就一定要使用这种模式作为日志模式,但是需要小心注入攻击。 使用slf4j-api 和 slf4j-simple的方式输出日志,不需要导入中间的适配器,当多个log实现框架同时存在的时候,会默认先使用slf4j-simple框架。
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jTest {
private static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);
@Test
public void test01() {
// 日志输出,默认会输出info级别以上的日志
LOGGER.error("error");
LOGGER.warn("warn");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
// 使用占位符输出日志信息
String name = "Peter";
Integer age = 10;
LOGGER.info("User:{}, Age:{}", name, age);
}
}
Log4j2的使用
简介
Apache Log4j2 是 log4j 的升级版,参考了logback的优秀设计,优化并解决了很多问题: 异常处理,在logback中,Appender中的异常不会被应用感知到,但是在log4j2中提供了一些异常处理机制。 性能提升,log4j2相比于logback和log4j在性能上有了很大的提升。 自动重载配置,提供了自动刷新参数配置,可以再生产上动态修改日志的级别而不需要重启应用。 无垃圾机制,log4j2在大部分情况下,使用自动垃圾清收机制,避免日志频繁的GC。 Log4j2 也可以做为日志的门面,但是因为其实现功能强大,但是门面略弱,所以当前主流的日志框架都是使用slf4j作为日志的门面,使用Log4j2作为日志的实现,通过连接器绑定,进行工作。 使用log4j-api和log4j-core的日志架构如下:
依赖信息:
<!--导入日志门面-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.13.3</version>
</dependency>
<!--导入日志实现-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.3</version>
</dependency>
Demo
package logger;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;
public class Log4j_Api_Core {
private static final Logger LOGGER = LogManager.getLogger(Log4j_Api_Core.class);
@Test
public void test02(){
// 日志输出,默认会输出info级别以上的日志
LOGGER.fatal("fatal");
LOGGER.error("error");
LOGGER.warn("warn");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
// 使用占位符输出日志信息
String name = "Peter";
Integer age = 10;
LOGGER.info("User:{}, Age:{}", name, age);
}
}
异步日志(效率提升的原因)
同步日志:
正常的日志,在线程需要打印日志的时候,会走一套流程,初始化,并生成日志,判断等级是否过滤等等,最后决定是否输出异步日志:
异步日志
异步日志的使用方式是在主线程需要生成日志之后,就将信息传递给一个阻塞的消息队列ArrayBlockingQueue,这个队列将启动一个(或多个)新的线程,完成后续日志的生成,所以效率高了很多
性能图表:
通常使用全局异步日志的效率最高,使用异步日志和同步日志混合的效率其次,使用同步日志的效率最低。通常情况下,都会直接使用异步日志提高效率。
使用Log4j2和slf4j的日志架构
当前最主流的日志使用方式事Log4j2作为实现,slf4j作为门户。
包引入
这个组合需要引入4个包,分别为Log4j2的门户和实现,slf4j的门户,slf4j和Log4j2的连接器。 之后就可以按照slf4j的所有实现模式和门户模式进行操作了。
<!--导入slf4j日志门面-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<!--导入log4j2日志实现-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.3</version>
</dependency>
<!--导入log4j2日志门面-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.13.3</version>
</dependency>
<!--使用适配器-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.9.1</version>
</dependency>
Domo实现
使用slf4j和log4j2的组合,Demo如下:
package logger;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4j_Log4j2 {
private static final Logger LOGGER = LoggerFactory.getLogger(Slf4j_Log4j2.class);
@Test
public void test03() {
// 日志输出,默认会输出info级别以上的日志
LOGGER.error("error");
LOGGER.warn("warn");
LOGGER.info("info");
LOGGER.debug("debug");
LOGGER.trace("trace");
// 使用占位符输出日志信息
String name = "Peter";
Integer age = 10;
LOGGER.info("User:{}, Age:{}", name, age);
}
}
Log4j2的配置文件
log4j2 默认会加载在resources下的xml配置文件,名称为log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--status = debug 日志框架本身的输出的日志级别,不是我们定义的日志,而是框架自身日志输出的级别-->
<!--monitorInterval 自动加载配置文件的间隔时间,不低于5s,可以实现热更新-->
<Configuration status="debug" monitorInterval="5">
<!-- 集中配置属性进行管理 可以定义各种名称和对应的值 -->
<properties>
<property name="LOG_HOME">E:/logs</property>
<property name="FILE_NAME">mylog</property>
<property name="log.sql.level">info</property>
</properties>
<!--日志处理-->
<Appenders>
<!--控制台输出结果 appender-->
<!--target 为日志的等级,如果为 SYSTEM_OUT 是普通日志,为白色;如果是SYSTEM_ERR,则为报警颜色,红色-->
<Console name="Console" target="SYSTEM_OUT">
<!--日志输出的格式-->
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %l - %msg%n"/>
</Console>
<!-- 日志的文件输出 -->
<File name="file" fileName="${LOG_HOME}/myfile.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n"/>
</File>
<!--随机读写流实现文件日志的输出,性能得到了提高,功能和 name = file是一样的-->
<RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n"/>
</RandomAccessFile>
<!--按照一定的规则拆分日志文件的(比如按照天,按照小时拆分日志) appender-->
<RollingRandomAccessFile name="RollingRandomAccessFile" fileName="${LOG_HOME}/${FILE_NAME}.log"
filePattern="${LOG_HOME}/$${date:yyyy-MM}/${FILE_NAME}-%d{yyyy-MM-dd HH-mm}-%i.log">
<!--日志级别的过滤器-->
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n"/>
<Policies>
<!--在系统启动时,触发拆分规则,生产一个新的日志文件-->
<OnStartupTriggeringPolicy/>
<!--按照时间的节点进行拆分,规则根据filePattern定义-->
<TimeBasedTriggeringPolicy interval="1"/>
<!--按照10Mb大小为一个文件的上限-->
<SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
<!--最多可以生成多少个日志文件,超出之后,最旧的会被覆盖-->
<DefaultRolloverStrategy max="20"/>
</RollingRandomAccessFile>
</Appenders>
<!--Logger定义-->
<Loggers>
<!--日志输出的等级,超过该等级的进行输出-->
<Root level="info">
<!--指定日志的处理器-->
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingRandomAccessFile"/>
<AppenderRef ref="file"/>
<AppenderRef ref="accessFile"/>
</Root>
</Loggers>
</Configuration>
Log4j2配置异步日志
异步配置有两种方式,全局异步和局部异步(异步和同步混合的方式)。局部异步的方式,如果处理不好,可能性能比完全同步效率还低,因此,仅介绍全局异步的使用方式。
需要引入一个新的jar包,用于异步日志生成;
<!--异步log4j2日志的依赖包-->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.4</version>
</dependency>
之后在resources下,定义一个文件 log4j2.component.properties 在这个文件中启动全局异步日志的指令即可:
log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
Log4j2无垃圾机制
在log4j2的2.6版本之后,会启动一个无垃圾机制,它会自动的清除日志中的垃圾,减少了系统自动GC的频率,大大提高了速度(无垃圾机制自动开启,不需要手动操作)。