Logback的StaticLoggerBinder

Logback的StaticLoggerBinder

image-20231016145915194

从图中可以看到,StaticLoggerBinder实现了LoggerFactory接口,提供了getLoggerFactory()方法。LoggerContext实现了ILoggerFactory接口,提供了getLogger(String name)方法。StaticLoggerBinder拥有成员变量LoggerContextContextSelectorStaticBinderStaticLoggerBinder委托ContextInitializer来初始化LoggerContext和委托ContextSelectorStaticBinder来选择一个上下文。

下面就来具体看看StaticLoggerBinder的代码 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* The unique instance of this class.
* 饿汉单例
*
*/
private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();

/**
* 私有静态对象
* 只有该类能访问的对象
* 用来做权限控制 后续只能该类来调用
*/
private static Object KEY = new Object();

/**
* 初始化该对象
*/
static {
SINGLETON.init();
}

/**
* 该对象是否初始化
*/
private boolean initialized = false;
/**
* ILoggerFactory的实现类
*/
private LoggerContext defaultLoggerContext = new LoggerContext();
/**
* 上下文选择器绑定者
* 通过 把defaultLoggerContext传递给ContextSelectorStaticBinder
* ContextSelectorStaticBinder 选择创建不同的ContextSelector 来获取不同环境的LoggerContext
* 选择策略 ContextSelector接口的实现有 ContextJNDISelector 和 DefaultContextSelector 策略模式的应用
* ContextSelectorStaticBinder 作用主要是来选择策略
* 这种实现方式可以用来借鉴
*/
private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder.getSingleton();
1
2
3
4
private StaticLoggerBinder() {
//为上下文设置名称
defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
 /**
*提供getSingleton()方法是对接slf4j的强制要求
*/
public static StaticLoggerBinder getSingleton() {
return SINGLETON;
}

/**
* Package access for testing purposes.
* 重置
*/
static void reset() {
SINGLETON = new StaticLoggerBinder();
SINGLETON.init();
}

/**
* Package access for testing purposes.
*
* 1.委托ContextInitializer对象初始化LoggerContext
* 2.判断上下文是否有状态监听器 如果没有就用StatusPrinter打印上下文中的警告和错误状态
* 3.把defaultLoggerContext传递给ContextSelectorStaticBinder,选择一个contextSelector 初始化成员变量
* 在getLoggerFactory()方法中通过contextSelectorBinder.getContextSelector().getLoggerContext()来获取loggerContext
*/
void init() {
try {
try {
//委托ContextInitializer类对defaultLoggerContext进行初始化
//这里如果找到了任一配置文件,就会根据配置文件去初始化LoggerContext,如果没找到,会使用默认配置。
new ContextInitializer(defaultLoggerContext).autoConfig();
} catch (JoranException je) {
Util.report("Failed to auto configure default logger context", je);
}
// logback-292
if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
}
//对ContextSelectorStaticBinder类进行初始化
contextSelectorBinder.init(defaultLoggerContext, KEY);
initialized = true;
} catch (Exception t) { // see LOGBACK-1159
Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t);
}
}

/**
* LoggerFactoryBinder接口的实现方法 获取ILoggerFactory的实现类
* 1.判断是否已经初始化 若没有则返回defaultLoggerContext
* 2.若已经初始化 则通过contextSelectorBinder 返回loggerContext
* @return
*/
public ILoggerFactory getLoggerFactory() {
//如果initialized是false,那么会直接返回defaultLoggerContext
if (!initialized) {
return defaultLoggerContext;
}

//否则就委托刚才提到的ContextSelectorStaticBinder返回一个ContextSelector
if (contextSelectorBinder.getContextSelector() == null) {
throw new IllegalStateException("contextSelector cannot be null. See also " + NULL_CS_URL);
}
return contextSelectorBinder.getContextSelector().getLoggerContext();
}

public String getLoggerFactoryClassStr() {
return contextSelectorBinder.getClass().getName();
}

其中init()和getLoggerFactory()为核心方法。

这个初始化方法init()里做了2件事
第一件事是委托ContextInitializer类对defaultLoggerContext进行初始化。这里如果找到了任一配置文件,就会根据配置文件去初始化LoggerContext,如果没找到,会使用默认配置。关于如何根据配置文件进行配置上下文的,在后面的博客中介绍,这里先略过。
第二件事是对ContextSelectorStaticBinder类进行初始化。

再来大致看一下ContextSelectorStaticBinder的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 饿汉单例
*/
static ContextSelectorStaticBinder singleton = new ContextSelectorStaticBinder();

/**
* 上下文选择器
*/
ContextSelector contextSelector;
/**
* 用来做权限控制
*/
Object key;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
* FOR INTERNAL USE. This method is intended for use by StaticLoggerBinder.
*
* 内部使用,这个方法是给StaticLoggerBinder类来调用的
* 这用权限控制的方式可以用来借鉴
* @param defaultLoggerContext
* @throws ClassNotFoundException
* @throws NoSuchMethodException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public void init(LoggerContext defaultLoggerContext, Object key) throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
/**
* key 用来做权限控制 当key对象初始化后不能变更
* 在StaticLoggerBinder中private static Object KEY = new Object();初始化一个静态对象key
* 则static 只有StaticLoggerBinder类能够调用该init()
*/
if (this.key == null) {
this.key = key;
} else if (this.key != key) {
throw new IllegalAccessException("Only certain classes can access this method.");
}

String contextSelectorStr = OptionHelper.getSystemProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR);
if (contextSelectorStr == null) {
contextSelector = new DefaultContextSelector(defaultLoggerContext);
} else if (contextSelectorStr.equals("JNDI")) {
// if jndi is specified, let's use the appropriate class
contextSelector = new ContextJNDISelector(defaultLoggerContext);
} else {
contextSelector = dynamicalContextSelector(defaultLoggerContext, contextSelectorStr);
}
}

/**
* Instantiate the context selector class designated by the user. The selector
* must have a constructor taking a LoggerContext instance as an argument.
*
* @param defaultLoggerContext
* @param contextSelectorStr
* @return an instance of the designated context selector class
* @throws ClassNotFoundException
* @throws SecurityException
* @throws NoSuchMethodException
* @throws IllegalArgumentException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
static ContextSelector dynamicalContextSelector(LoggerContext defaultLoggerContext, String contextSelectorStr) throws ClassNotFoundException,
SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException,
InvocationTargetException {
Class<?> contextSelectorClass = Loader.loadClass(contextSelectorStr);
Constructor cons = contextSelectorClass.getConstructor(new Class[] { LoggerContext.class });
return (ContextSelector) cons.newInstance(defaultLoggerContext);
}

public ContextSelector getContextSelector() {
return contextSelector;
}

如果系统参数中配置了JNDI,这里会得到一个ContextJNDISelector,实际应用中,一般会得到一个DefaultContextSelector,并且把已经初始化完成的defaultLoggerContext传给新创建的这个DefaultContextSelector。

总结一下这个大致流程

  1. StaticLoggerBinder委托ContxetInitializer去初始化上下文,这个时候会去读取配置文件,并根据配置文件对LoggerContext进行初始化
  2. 然后初始化ContextSelectorStaticBinder,选择一个合适的ContextSelector并把defaultLoggerContext传递给它。
  3. 调用getLoggerFactory()方法,若未初始化返回defaultLoggerContext,否则委托ContextSelectorStaticBinder获取一个ContextSelector返回一个上下文。