SLF4J--⾃动绑定实现类原理(源码)
⼀、概述
slf4j(全称是Simple Loging Facade For Java)是⼀个为Java程序提供⽇志输出的统⼀接⼝,并不是⼀个具体的⽇志实现⽅案,就好像我们经常使⽤的JDBC⼀样,只是⼀种接⼝规则定义⽽已,使⽤了slf4j可以对客户端应⽤。因为当我们在代码实现中引⼊log⽇志的时候,⽤的是接⼝,所以可以实时的更具情况来调换具体的⽇志实现类。这就是slf4j的作⽤。基于SLF4J⽇志实现⽅案,⽐如apache的
log4j,log4j2,logback,jdk⾃带的java.util.logging.Logger等等。
其中对应的实现jar包:
jar/log4j-slf4j-impl是使⽤org.apache.log4j.Logger提供的驱动
jar是使⽤java.util.logging提供的驱动
jar直接绑定
jar是使⽤commons-logging提供的驱动
jar是使⽤logback提供的驱动
⼆、⼯作原理窥探
1. 简单实现
1 1 st.slf4j;
2 2 import org.slf4j.Logger;
3 3 import org.slf4j.LoggerFactory;
4 4 public class TestLog4j {
5 5 private static final Logger LOGGER = Logger(TestLog4j.class);
6 6
7 7 public static void main(String[] args) {
8 8 String message = "服务器出错啦.";
9 9 LOGGER.info("Error message is: {}", message);
1010 }
1111 }
2. 通过LoggerFactory类的静态getLogger()获取logger。slf4j-api作为slf4j的接⼝类,使⽤在程序代码中,这个包提供了⼀个
Logger类和LoggerFactory类,Logger类⽤来打⽇志,LoggerFactory类⽤来获取Logger;⽐如说:slf4j-log4j,这个包是连接slf4j和log4j的桥梁,怎么连接的呢?我们看看slf4j的LoggerFactory类的getLogger函数的源码:
3. 我本地同时引⼊了log4j2和logback包,即可看到实现。可以看出log4j-slf4j提供了该org.slf4j.impl.StaticLoggerBinder静态类来
实现slf4和log4j的桥接
4. 再看看slf4j-log4j包种的这个StaticLoggerBinder类创建ILoggerFactory长什么样⼦
1
public static Logger getLogger(String name) {2
ILoggerFactory iLoggerFactory = getILoggerFactory();3
Logger(name);4
}5
6
public static ILoggerFactory getILoggerFactory() {7
if (INITIALIZATION_STATE == UNINITIALIZED) {8
synchronized (LoggerFactory.class) {9
if (INITIALIZATION_STATE == UNINITIALIZED) {10
INITIALIZATION_STATE = ONGOING_INITIALIZATION;11
performInitialization();12
}13
}14
}15
switch (INITIALIZATION_STATE) {16
case SUCCESSFUL_INITIALIZATION:17
Singleton().getLoggerFactory(); // StaticLoggerBinder ⾮SLF4J 提供,是由具体的实现包,如log4j ,logback 来提供18
case NOP_FALLBACK_INITIALIZATION:19
return NOP_FALLBACK_FACTORY;20
case FAILED_INITIALIZATION:21
throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);22
case ONGOING_INITIALIZATION:23
// support re-entrant behavior.24
// See also jira.qos.ch/browse/SLF4J-9725
return SUBST_FACTORY;26
}27
throw new IllegalStateException("Unreachable code");28 }29
1package org.slf4j.impl;
2
3import org.apache.logging.slf4j.Log4jLoggerFactory;
4import org.slf4j.ILoggerFactory;
5import org.slf4j.spi.LoggerFactoryBinder;
6
7public final class StaticLoggerBinder implements LoggerFactoryBinder {
8 public static String REQUESTED_API_VERSION = "1.6";
9 private static final String LOGGER_FACTORY_CLASS_STR = Name();
10 private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
11 private final ILoggerFactory loggerFactory = new Log4jLoggerFactory();
12
13 private StaticLoggerBinder() {
14 }
15
16 public static StaticLoggerBinder getSingleton() {
17 return SINGLETON;
18 }
19
20 public ILoggerFactory getLoggerFactory() {
21 return this.loggerFactory;
22 }
23
24 public String getLoggerFactoryClassStr() {
25 return LOGGER_FACTORY_CLASS_STR;
26 }
27}
5. 可以看到slf4j-log4j中的StaticLoggerBinder类创建的ILoggerFactory其实是⼀个org.slf4j.impl.Log4jLoggerFactory,这个类的
getLogger函数代码如下:
1package org.apache.logging.slf4j;
2
3import org.apache.logging.log4j.LogManager;
4import org.apache.logging.log4j.spi.AbstractLoggerAdapter;
5import org.apache.logging.log4j.spi.LoggerContext;
6import org.apache.logging.log4j.util.StackLocatorUtil;
7import org.apache.logging.slf4j.Log4jLogger;
8import org.slf4j.ILoggerFactory;
9import org.slf4j.Logger;
10
11public class Log4jLoggerFactory extends AbstractLoggerAdapter<Logger> implements ILoggerFactory {
12 private static final String FQCN = Name();
13 private static final String PACKAGE = "org.slf4j";
14
15 public Log4jLoggerFactory() {
16 }
17
18 protected Logger newLogger(String name, LoggerContext context) {
19 String key = "ROOT".equals(name)?"":name;
20 return new Logger(key), name);
21 }
22
23 protected LoggerContext getContext() {
24 Class anchor = CallerClass(FQCN, "org.slf4j");
25 return anchor == Context():CallerClass(anchor));
26 }
27}
三、slf4j加载实现类顺序问题
1. 默认来说,slf4j的加载实现类是随机的,如发现有多个org.slf4j.impl.StaticLoggerBinder,启动应⽤的时候会出现下⾯警告:
2. 从源码上来看,在getILoggerFactory()⽅法中,初始化会调⽤performInitialization⽅法进⾏检测实现类,最终会调
⽤reportMultipleBindingAmbiguity ⽅法打印出警告信息
四、log4j2简单例⼦
1. log4j2的MDC配置
1
static Set<URL> findPossibleStaticLoggerBinderPathSet() {2
Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();3
try {4
ClassLoader loggerFactoryClassLoader = ClassLoader();5
Enumeration<URL> paths;6
if (loggerFactoryClassLoader == null) {7
paths = SystemResources(STATIC_LOGGER_BINDER_PATH);8
} else {9
paths = Resources(STATIC_LOGGER_BINDER_PATH);10
}11
while (paths.hasMoreElements()) {12
URL path = Element();13
staticLoggerBinderPathSet.add(path);14
}15
} catch (IOException ioe) {16
}18
return staticLoggerBinderPathSet;19
}20
21
private static void reportMultipleBindingAmbiguity(Set<URL> binderPathSet) {22
if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {23
for (URL path : binderPathSet) {25
}27
}29
}30
31
private static void reportActualBinding(Set<URL> binderPathSet) {32
// binderPathSet can be null under Android 33
if (binderPathSet != null && isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {34
1public final class ContextLog4j2Util {
2
3 /**
4 * 将上下⽂环境变量信息加⼊到MDC中,以便打印到⽇志
5 */
6 public static void addContext2MDC() {
7 MDC.put(ContextConstKey.REQUEST_NO, StringUtils.RequestNo()) ? "" : RequestNo());
8 MDC.put(ContextConstKey.CONSUMER_IP, StringUtils.ConsumerIp()) ? "" :
9 MDC.put(ContextConstKey.CHANNEL_CODE, StringUtils.ChannelCode()) ? "" : ChannelCode());
10 MDC.put(ContextConstKey.LOCAL_IP, LocalHost());
11 }
12
13 /**
14 * 从MDC清理对象
15 */
16 public static void removeContextFromMDC() {
17 ve(ContextConstKey.REQUEST_NO);
18 ve(ContextConstKey.CONSUMER_IP);
19 ve(ContextConstKey.CHANNEL_CODE);
20 ve(ContextConstKey.REMOTE_IP);
21 ve(ContextConstKey.LOCAL_IP);
22 }
23}
2. l配置
1<?xml version="1.0" encoding="UTF-8"?>
2<configuration status="WARN" shutdownHook="disable">
3 <Properties>
4 <Property name="sysName">test-app</Property>
5 <Property name="bizLogLevel">INFO</Property>
6 <Property name="otherLogLevel">INFO</Property>
7 <Property name="logFilePath">/home/q/test/test-app/logs</Property>
8 <Property name="logSize">1000 MB</Property>
9 <Property name="maxFile">100</Property>
10 <Property name="errorLogFile">${sysName}_err</Property>
11 <Property name="bizLogFile">${sysName}_biz</Property>
12 <Property name="allLogFile">${sysName}_all</Property>
13 <Property name="monitorLogFile">${sysName}_monitor</Property>
14 <Property name="ioLogFile">${sysName}_io</Property>
15 <Property name="dcLogFile">${sysName}_dc</Property>
16 <Property name="rdsLogFile">${sysName}_rds</Property>
17 <Property name="threadLogFile">${sysName}_thread</Property>
18 <Property name="tempLogFile">${sysName}_temp</Property>
19 </Properties>
20 <appenders>
21 <Console name="Console" target="SYSTEM_OUT">
22 <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
23 <PatternLayout charset="UTF-8"
24 pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%t] [%X{requestNo}|%X{consumerIp}|%X{sysChannelCode}|%X{localIp}] [%
25 </Console>
26 <RollingFile name="ErrorRollingFile" fileName="${logFilePath}/${errorLogFile}.log"
27 filePattern="${logFilePath}/$${date:yyyy-MM}/${errorLogFile}-%d{MM-dd-yyyy}-%">
log4j2不打印日志28 <PatternLayout charset="UTF-8"
29 pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%t] [%X{requestNo}|%X{consumerIp}|%X{sysChannelCode}|%X{localIp}] [%
30 <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
31 <Policies>
32 <TimeBasedTriggeringPolicy/>
33 <SizeBasedTriggeringPolicy size="${logSize}"/>
34 </Policies>
35 <DefaultRolloverStrategy max="${maxFile}"/>
36 </RollingFile>
37 <RollingFile name="BizRollingFile" fileName="${logFilePath}/${bizLogFile}.log"
38 filePattern="${logFilePath}/$${date:yyyy-MM}/${bizLogFile}-%d{MM-dd-yyyy}-%">
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论