slf4j
slf4j - simple log facade for java
门面模式
slf4j是门面模式的典型应用
门面模式的核心是“外部与一个子系统的通信必须通过一个统一的外观对象进行,使得子系统更易于使用”
门面模式的核心为Facade即门面对象,门面对象核心:
- 知道所有子角色的功能和责任
- 将客户端发来的请求委派到子系统中,没有实际业务逻辑
- 
为什么要使用slf4j举个栗子 
 我们的系统使用了logback这个日志系统,我们的系统又使用了A.jar,A.jar中使用的日志系统为log4j,我们的系统又使用了B.jar,B.jar中使用的日志系统为slf4j-simple,这样我们的系统就必须同时支持和维护logback/log4j/slf4j-simple三种日志框架,非常不便。
 为了解决上述的问题,可以通过引入一个适配层,由适配层决定使用哪一种日志系统,而调用端只用打印日志即可,不需要关心如何打印日志,slf4j就是这种适配层。
 slf4j只是一个日至标准,并不是日志系统的具体实现,只做一下两件事情:
- 提供日志接口 
- 提供获取具体日志对象的方法slf4j-simple、logback都是slf4j的具体实现,log4j并不直接实现slf4j,但是有专门的一层桥接slf4j-log4j12来实现slf4j。 
slf4j好处
- 解耦合日志和项目
 slf4j是一个适配器,我们通过slf4j的日志方法统一打印日志时,可以忽略其他日志的具体方法,当系统切换日志源时不需要更改代码。
- 节省内存
 log4j这些传统的日志系统里面没有占位符的概念,需要打印信息的时候,不得不创建无用String对象来进行输出信息的拼接。在项目中使用slf4j1. 导入相关依赖- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.13</version>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <version>1.7.13</version>
- </dependency>
 - <!-- 引入log4j日志依赖 -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-log4j</artifactId>
- <version>1.3.8.RELEASE</version>
- </dependency>
 2. 创建Logger对象```xml import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory;
/**
- @author liuwenxiu
- @Description:
- @date 2022-7-6 18:39 */ public class LogTest { private static final Logger logger = LoggerFactory.getLogger(LogTest.class); - @Test public void testLog() { - logger.error("错误信息为:{}","Something error happen...");
 - } 
}
> 注意点:项目中的Logger对象可能不止一个,注意在导包的时候导入**org.slf4j的Logger对象**
<a name="zRHHf"></a>
#### 对象修饰符注意点
在创建对象时一般会使用**private static final**来修饰日志对象,原因如下
- private => 防止其他类使用当前类的日志对象
- static => 让每个类中的日志对象只生成一份,日志对象是属于类的,不属于具体的实例
- final => 避免日志对象在运行时被修改
<a name="TSjo1"></a>
## slf4j实现原理
```java
Logger logger = LoggerFactory.getLogger(LogTest.class);
public static Logger getLogger(Class<?> clazz) {
Logger logger = getLogger(clazz.getName());
//...省略部分代码的展示
return logger;
}
// 此步骤就是根据Class对象获取其全限定名然后调用下面的方法
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
public static ILoggerFactory getILoggerFactory() {
if (INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
performInitialization();
}
//...省略部分代码的展示
}
/*
if(初始化状态 = 未初始化) {
初始化状态 = 正在进行初始化;
执行初始化();
}
*/
private final static void performInitialization() {
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
versionSanityCheck();
}
}
Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
private static Set<URL> findPossibleStaticLoggerBinderPathSet() {
// use Set instead of list in order to deal with bug #138
// LinkedHashSet appropriate here because it preserves insertion order during iteration
Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
try {
ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
Enumeration<URL> paths;
if (loggerFactoryClassLoader == null) {
paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
} else {
paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
}
while (paths.hasMoreElements()) {
URL path = paths.nextElement();
staticLoggerBinderPathSet.add(path);
}
} catch (IOException ioe) {
Util.report("Error getting resources from path", ioe);
}
return staticLoggerBinderPathSet;
}
// 核心代码是第11行
// 在getLogger时会去classpath下找STATIC_LOGGER_BINDER_PATH
// STATIC_LOGGER_BINDER_PATH的值是org/slf4j/impl/StaticLoggerBinder.class
// 即所有slf4j的实现在提供的jar包路径下都存在上述的这个类
// 我们不能避免在系统中同时引入多个slf4j的实现,所以接收的地方是一个Set。
Log4j
Log4J 是 Apache 的一个开源项目(官网 http://jakarta.apache.org/log4j),通过在项目中使用 Log4J,我们可以控制日志信息输出到控制台、文件、GUI 组件、甚至是数据库中。我们可以控制每一条日志的输出格式,通过定义日志的输出级别,可以更灵活的控制日志的输出过程,方便项目的调试。
Log4j的组成
- Loggers(日志记录器) - 控制日志的输出级别与日志是否输出
- Appenders(输出端) - 指定日志的输出方式,输出到控制台、文件等
- Layout(日志格式化器)  -  控制日志信息的输出格式
日志级别| 日志级别 | 含义 | | —- | —- | | OFF | 最高日志级别,关闭左右日志 | | FATAL | 将会导致应用程序退出的错误 | | ERROR | 发生错误事件,仍不影响系统的继续运行 | | WARN | 警告,即潜在的错误情形 | | INFO | 一般用于粗粒度级别上,强调应用程序的运行全程 | | DEBUG | 一般用于细粒度级别上,对调试应用程序非常有帮助 | | ALL | 最低等级,打开所有日志记录 |
注:一般只使用4个级别,优先级从高到低为 ERROR > WARN > INFO > DEBUG
Appender
| 输出端类型 | 作用 | 
|---|---|
| ConsoleAppender | 将日志输出到控制台 | 
| FileAppender | 将日志输出到文件中 | 
| DailyRollingFileAppender | 将日志输出到一个日志文件,并且每天输出到一个新的文件 | 
| RollingFileAppender | 将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大小达到指定尺寸时,会自动把文件改名,同时产生一个新的文件 | 
| JDBCAppender | 把日志信息保存到数据库中 | 
Layout
| 格式化器类型 | 作用 | 
|---|---|
| HTMLLayout | 格式化日志输出为HTML表格形式 | 
| SimpleLayout | 简单的日志输出格式化,打印的日志为(info-message) | 
| PatternLayout | 最强大的格式化日期,可以根据自定义格式输出日志,如果没有指定转换格式,就是用默认的转换格式。 | 
slf4j和log4j的区别
- log4j(log for java)
 Apache的一个开源项目,可以灵活地记录日志信息,通过log4j的配置文件灵活配置日志的记录格式、记录级别、输出格式,而不需要修改已有的日志记录代码。
- slf4j(simple log facade for java)
 slf4j不是具体的日志解决方案,它只服务于各种各样的日志系统。按照官方的说法,slf4j是一个用于日志系统的简单Facade,允许用户在部署其应用时所希望的日志系统。
可以将log4j看做一个完整的日志库,而slf4j是一个日志库的规范接口。
log4j2的配置文件
基础信息
配置文件的格式:xml形式/json格式
配置文件的位置:log4j2默认会在classpath目录下寻找log4j2.xml、log4j2.json、log4j.jsn等名称的文件
系统选择配置文件的优先级从先到后如下所示:
- log4j2-test.json或者log4j2-test.jsn文件
- log4j2-test.xml文件
- log4j2.json或log4j2.jsn文件
- log4j2.xml文件一般在开发时默认使用log4j2.xml进行命名 
配置示例
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration status="WARN" monitorInterval="30">
<!--先定义所有的appender-->
<appenders>
<!--这个输出控制台的配置-->
<console name="Console" target="SYSTEM_OUT">
<!--输出日志的格式-->
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
</console>
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用-->
<File name="log" fileName="log/test.log" append="false">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
<!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
<RollingFile name="RollingFileWarn" fileName="${sys:user.home}/logs/warn.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 -->
<DefaultRolloverStrategy max="20"/>
</RollingFile>
<RollingFile name="RollingFileError" fileName="${sys:user.home}/logs/error.log"
filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log">
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<loggers>
<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
<logger name="org.springframework" level="INFO"></logger>
<logger name="org.mybatis" level="INFO"></logger>
<root level="all">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileInfo"/>
<appender-ref ref="RollingFileWarn"/>
<appender-ref ref="RollingFileError"/>
</root>
</loggers>
</configuration>
配置信息
Configuration节点
子节点:Appenders和Loggers
属性:status和monitorinterval
- status:指定log4j本身打印日志的级别
- monitorinterval:指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s - Appenders节点- Console节点- 子节点:ThresholdFilter、PatternLayout 
 属性:name、target
- name:指定Appender的名字 
- target:SYSTEM_OUT或SYSTAEM_ERR,一般只设置默认SYSTEM_OUT - ThresholdFilter节点- 临界值过滤器,过滤掉低于指定临界值的日志 
 属性:onMatch、onMismatch
- onMatch:表示匹配设定的日志级别后是DENY还是ACCEPT 
- onMismatch:表示不匹配设定的日志级别是DENY、ACCEPT还是NEUTRAL - PatternLayout节点- 属性:charset、pattern 
- charset:设置语言,常用”UTF-8” 
- 
File节点定义输出到指定位置文件的Appender 
 子节点:PatternLayout
 属性:name、fileName
- 
RollingFile节点定义超过指定大小自动删除旧的创建新的Appender 
 子节点:ThresholdFilter、PatternLayout、Policies、DefaultRolloverStrategy
 属性:name、fileName、filePattern
- 
Policies节点子节点:TimeBasedTriggeringPolicy、SizeBasedTriggeringPolicy 
- TimeBasedTriggeringPolicy:基于时间的滚动策略 
 interval属性用来指定多久滚动一次,默认是1小时
 modulate=true用来调整时间:比如现在是3:00am,interval是4,那么第一次滚动在4:00am,接着是8:00am,12:00am…而不是7:00am
- SizeBasedTriggeringPolicy:基于指定文件大小的滚动策略 
 size属性用来指定每个日志文件的大小- DefaultRolloverStrategy节点- 指定同一个文件夹下最多有几个日志文件时开始删除最旧的,创建新的(通过max属性) - Loggers节点- Root节点- 指定项目的根日志,如果没有单独指定Logger,默认使用该Root日志输出 
 level:日志输出级别,共有八个级别,从低到高为:ALL- Logger节点- 单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等 
 属性:level、name
- name:用来指定该Logger所适用的类或者类所在的包的全路径,继承自Root节点 
子节点:AppenderRef
- 指定该日志输出到哪个Appender,如果没有指定,就会默认继承自Root。如果指定了,会在指定的这个Appender和Root的Appender中都会输出。如果设置Logger的additivity=”false”则只会在自定义的Appender中输出。
配置参数详解%d{HH:mm:ss.SSS} 表示输出到毫秒的时间
 %t 输出当前线程名称
 %-5level 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0
 %logger 输出logger名称,因为Root Logger没有名称,所以没有输出
 %msg 日志文本
 %n 换行
 其他常用的占位符有:
 %F 输出所在的类文件名,如Log4j2Test.java
 %L 输出行号
 %M 输出所在方法名
 %l 输出语句所在的行数, 包括类名、方法名、文件名、行数日志Level| 级别 | 含义 | | —- | —- | | ALL | 最低级别,用于打开所有的日志记录 | | Trace | 追踪,程序推进下可以写个trace输出 | | Debug | 一般用于细粒度级别上,对调试应用程序非常有帮助 | | Info | 一般用于粗粒度级别上,强调应用程序的运行全程 | | Warn | 输出警告及warn以下级别的日志 | | Error | 输出错误信息日志 | | Fatal | 输出每个严重的错误事件将会导致应用程序退出的日志 | | OFF | 最高级别,关闭所有日志记录 |
 
 
                         
                                

