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小时内删除。