本文轉自上善若水的博客,原文出處:http://www.blogjava.net/DLevin/archive/2012/07/10/382678.html。感謝做者的無私分享。html
LoggerRepository從字面上理解,它是一個Logger的容器,它會建立並緩存Logger實例,從而具備相同名字的Logger實例不會屢次建立,以提升性能。它的這種特性有點相似Spring的IOC概念。Log4J支持兩種配置文件:properties文件和xml文件。Configurator解析配置文件,並將解析後的信息添加到LoggerRepository中。LogManager最終將LoggerRepository和Configurator整合在一塊兒。java
LoggerRepository接口
LoggerRepository是一個Logger的容器,它負責建立、緩存Logger實例,同時它也維護了Logger之間的關係,由於在Log4J中,全部Logger都組裝成以RootLogger爲根的一棵樹,樹的層次由Logger的Name來決定,其中以’.’分隔。apache
除了作爲一個Logger容器,它還有一個Threshold屬性,用於過濾全部在Threshold級別如下的日誌。以及其餘和Logger操做相關的方法和屬性。
緩存
LoggerRepository的接口定義以下:
app
1
public
interface LoggerRepository {
2
public
void addHierarchyEventListener(HierarchyEventListener listener);
3
boolean isDisabled(
int level);
4
public
void setThreshold(Level level);
5
public
void setThreshold(String val);
6
public
void emitNoAppenderWarning(Category cat);
7
public Level getThreshold();
8
public Logger getLogger(String name);
9
public Logger getLogger(String name, LoggerFactory factory);
10
public Logger getRootLogger();
11
public
abstract Logger exists(String name);
12
public
abstract
void shutdown();
13
public Enumeration getCurrentLoggers();
14
public
abstract
void fireAddAppenderEvent(Category logger, Appender appender);
15
public
abstract
void resetConfiguration();
16 }
Hierarchy類
Hierarchy是Log4J中默認對LoggerRepository的實現類,它用於表達其內部的Logger是以層次結構存儲的。在對LoggerRepository接口的實現中,getLogger()方法是其最核心的實現,於是首先從這個方法開始。less
Hierarchy中用一個Hashtable來存儲全部Logger實例,它以CategoryKey做爲key,Logger做爲value,其中CategoryKey是對Logger中Name字符串的封裝,之因此要引入這個類是出於性能考慮,由於它會緩存Name字符串的hash code,這樣在查找過程當中計算hash code時就能夠直接取得而不用每次都計算。ide
1
class CategoryKey {
2 String name;
3
int hashCache;
4
5 CategoryKey(String name) {
6
this.name = name;
7 hashCache = name.hashCode();
8 }
9
final
public
int hashCode() {
10
return hashCache;
11 }
12
final
public
boolean equals(Object rArg) {
13
if (
this == rArg)
14
return
true;
15
if (rArg !=
null && CategoryKey.
class == rArg.getClass())
16
return name.equals(((CategoryKey) rArg).name);
17
else
18
return
false;
19 }
20 }
getLogger()方法中有一個重載函數提供LoggerFactory接口,它用於沒有在LoggerRepository中找到Logger實例時建立相應的Logger實例,默認實現直接建立一個Logger實例,用戶能夠經過自定義LoggerFactory實現建立本身的Logger實例。函數
1
public
interface LoggerFactory {
2
public Logger makeNewLoggerInstance(String name);
3 }
4
class DefaultCategoryFactory
implements LoggerFactory {
5
public Logger makeNewLoggerInstance(String name) {
6
return
new Logger(name);
7 }
8 }
getLogger()方法首先根據傳入name建立CategoryKey實例,然後從緩存ht字段中查找:oop
1. 若是找到對應的Logger實例,則直接返回該實例。性能
2. 若是沒有找到任何實例,則使用LoggerFactory建立新的Logger實例,並將該實例緩存到ht集合中,同時更新新建立Logger實例的parent屬性。更新parent屬性最簡單的作法是從後往前以’.’爲分隔符截取字符串,使用截取後的字符串從ht集合中查找是否存在Logger實例,若是存在,則新建立的Logger實例的parent即爲找到的實例,若在整個遍歷過程當中都沒有找到相應的parent實例,則其parent實例爲root。然而若是一個「x.y.z.w」Logger起初的parent設置爲root,然後出現「x.y.z」Logger實例,那麼就須要更新「x.y.z.w」Logger的parent爲「x.y.z」Logger實例,此時就會遇到一個如何找到在集合中已經存在的「x.y.z」Logger實例子節點的問題。固然一種簡單的作法是遍歷ht集合中全部實例,判斷那個實例是否是「x.y.z」Logger實例的子節點,是則更新其parent節點。因爲每次的遍歷會引發一些性能問題,於是Log4J使用ProvisionNode事先將全部的可能相關的子節點保存起來,並將ProvisionNode實例添加到ht集合中,這樣只要找到對應的ProvisionNode實例,就能夠找到全部相關的子節點了。好比對「x.y.z.w」Logger實例,它會產生三個ProvisionNode實例(固然若是相應的實例已經存在,則直接添加而無需建立,另外,若是相應節點已是Logger實例,那麼將「x.y.z.w」Logger實例的parent直接指向它便可):ProvisionNode(「x」), ProvisionNode(「x.y」), ProvisionNode(「x.y.z」),他們都存儲了「x.y.z.w」Logger實例做爲其子節點。
1
class ProvisionNode
extends Vector {
2 ProvisionNode(Logger logger) {
3
super();
4
this.addElement(logger);
5 }
6 }
7
final
private
void updateParents(Logger cat) {
8 String name = cat.name;
9
int length = name.length();
10
boolean parentFound =
false;
11
//
if name = "x.y.z.w", loop thourgh "x.y.z", "x.y" and "x"
12
for (
int i = name.lastIndexOf('.', length - 1); i >= 0; i = name
13 .lastIndexOf('.', i - 1)) {
14 String substr = name.substring(0, i);
15 CategoryKey key =
new CategoryKey(substr);
16 Object o = ht.get(key);
17
if (o ==
null) {
18 ProvisionNode pn =
new ProvisionNode(cat);
19 ht.put(key, pn);
20 }
else
if (o
instanceof Category) {
21 parentFound =
true;
22 cat.parent = (Category) o;
23
break;
//
no need to update the ancestors of the closest
24
//
ancestor
25
}
else
if (o
instanceof ProvisionNode) {
26 ((ProvisionNode) o).addElement(cat);
27 }
else {
28 Exception e =
new IllegalStateException(
29 "unexpected object type " + o.getClass() + " in ht.");
30 e.printStackTrace();
31 }
32 }
33
//
If we could not find any existing parents, then link with root.
34
if (!parentFound)
35 cat.parent = root;
36 }
3. 若是找到的是ProvisionNode實例,首先使用factory建立新的Logger實例,將該實例添加到ht集合中,而後更新找到的ProvisionNode內部全部Logger的parent字段以及新建立Logger的parent字段。更新過程當中須要注意ProvisionNode中的Logger實例已經指向了正確的parent了,因此只要更新那些ProvisionNode中Logger實例指向的parent比新建立的Logger自己層次要高的那些parent屬性。好比開始插入「x.y.z」Logger實例,然後插入「x.y.z.w」Logger實例,此時ProvisionNode(「x」)認爲「x.y.z」Logger實例和「x.y.z.w」Logger實例都是它的子節點,然後插入「x」Logger實例,那麼只須要更新「x.y.z」Logger的父節點爲「x」Logger實例便可,而不用更新「x.y.z.w」Logger實例的父節點。
1
final
private
void updateChildren(ProvisionNode pn, Logger logger) {
2
final
int last = pn.size();
3
for (
int i = 0; i < last; i++) {
4 Logger l = (Logger) pn.elementAt(i);
5
//
Unless this child already points to a correct (lower) parent,
6
//
make cat.parent point to l.parent and l.parent to cat.
7
if (!l.parent.name.startsWith(logger.name)) {
8 logger.parent = l.parent;
9 l.parent = logger;
10 }
11 }
12 }
綜合起來,getLogger()方法的實現代碼以下:
1
public Logger getLogger(String name, LoggerFactory factory) {
2 CategoryKey key =
new CategoryKey(name);
3 Logger logger;
4
synchronized (ht) {
5 Object o = ht.get(key);
6
if (o ==
null) {
7 logger = factory.makeNewLoggerInstance(name);
8 logger.setHierarchy(
this);
9 ht.put(key, logger);
10 updateParents(logger);
11
return logger;
12 }
else
if (o
instanceof Logger) {
13
return (Logger) o;
14 }
else
if (o
instanceof ProvisionNode) {
15 logger = factory.makeNewLoggerInstance(name);
16 logger.setHierarchy(
this);
17 ht.put(key, logger);
18 updateChildren((ProvisionNode) o, logger);
19 updateParents(logger);
20
return logger;
21 }
else {
22
//
It should be impossible to arrive here
23
return
null;
//
but let's keep the compiler happy.
24
}
25 }
26 }
其餘的方法實現則比較簡單,對LoggerRepository來講,它也能夠像其註冊HierarchyEventListener監聽器,每當向一個Logger添加或刪除Appender,該監聽器就會觸發。
1
public
interface HierarchyEventListener {
2
public
void addAppenderEvent(Category cat, Appender appender);
3
public
void removeAppenderEvent(Category cat, Appender appender);
4 }
5
private Vector listeners;
6
public
void addHierarchyEventListener(HierarchyEventListener listener) {
7
if (listeners.contains(listener)) {
8 LogLog.warn("Ignoring attempt to add an existent listener.");
9 }
else {
10 listeners.addElement(listener);
11 }
12 }
13
public
void fireAddAppenderEvent(Category logger, Appender appender) {
14
if (listeners !=
null) {
15
int size = listeners.size();
16 HierarchyEventListener listener;
17
for (
int i = 0; i < size; i++) {
18 listener = (HierarchyEventListener) listeners.elementAt(i);
19 listener.addAppenderEvent(logger, appender);
20 }
21 }
22 }
23
void fireRemoveAppenderEvent(Category logger, Appender appender) {
24
if (listeners !=
null) {
25
int size = listeners.size();
26 HierarchyEventListener listener;
27
for (
int i = 0; i < size; i++) {
28 listener = (HierarchyEventListener) listeners.elementAt(i);
29 listener.removeAppenderEvent(logger, appender);
30 }
31 }
32 }
Hierarchy中保存了threshold字段,用戶能夠設置threshold。而對root實例,它在夠着Hierarchy時就被指定了。getCurrentLoggers()方法將ht集合中全部的Logger實例取出。shutdown()方法遍歷全部Logger實例以及root實例,調用全部附加其上的Appender的close()方法,並將全部Appender實例從Logger中移除,最後觸發AppenderRemove事件。resetConfiguration()方法將root字段初始化、調用shutdown()方法移除Logger中的全部Appender、初始化全部Logger實例當不將其從LoggerRepository中移除、清楚rendererMap和throwableRender中的數據。
RendererSupport接口
RendererSupport接口支持用戶爲不一樣的類設置相應的ObjectRender實例,從而能夠從被渲染的類中或許更多的信息而不是默認的調用其toString()方法。
1
public
interface RendererSupport {
2
public RendererMap getRendererMap();
3
public
void setRenderer(Class renderedClass, ObjectRenderer renderer);
4 }
5 Hierarchy類實現了RenderedSupprt接口,並且它的實現也很簡單:
6 RendererMap rendererMap;
7
public Hierarchy(Logger root) {
8
9 rendererMap =
new RendererMap();
10
11 }
12
public
void addRenderer(Class classToRender, ObjectRenderer or) {
13 rendererMap.put(classToRender, or);
14 }
15
public RendererMap getRendererMap() {
16
return rendererMap;
17 }
18
public
void setRenderer(Class renderedClass, ObjectRenderer renderer) {
19 rendererMap.put(renderedClass, renderer);
20 }
在RendererMap類實現中,它使用Hastable保存被渲染的類實例和相應的ObjectRender實例,在查找一個類是否存在註冊的渲染類時,若是它自己沒有找到,須要向上嘗試其父類和接口是否有註冊相應的ObjectRender類,若是都沒有找到,則返回默認的ObjectRender。
1
public ObjectRenderer get(Class clazz) {
2 ObjectRenderer r =
null;
3
for (Class c = clazz; c !=
null; c = c.getSuperclass()) {
4 r = (ObjectRenderer) map.get(c);
5
if (r !=
null) {
6
return r;
7 }
8 r = searchInterfaces(c);
9
if (r !=
null)
10
return r;
11 }
12
return defaultRenderer;
13 }
14 ObjectRenderer searchInterfaces(Class c) {
15 ObjectRenderer r = (ObjectRenderer) map.get(c);
16
if (r !=
null) {
17
return r;
18 }
else {
19 Class[] ia = c.getInterfaces();
20
for (
int i = 0; i < ia.length; i++) {
21 r = searchInterfaces(ia[i]);
22
if (r !=
null)
23
return r;
24 }
25 }
26
return
null;
27 }
28
public ObjectRenderer getDefaultRenderer() {
29
return defaultRenderer;
30 }
31
public
void put(Class clazz, ObjectRenderer or) {
32 map.put(clazz, or);
33 }
ThrowableRendererSupport接口
ThrowableRendererSupport接口用於支持設置和獲取ThrowableRenderer,從而用戶能夠自定義對Throwable對象的渲染。
1
public
interface ThrowableRendererSupport {
2 ThrowableRenderer getThrowableRenderer();
3
void setThrowableRenderer(ThrowableRenderer renderer);
4 }
Hierarchy類以屬性的方式實現了該接口,於是每一個Hierarchy實例只能有一個全局的ThrowableRenderer,而不能像ObjectRender那樣爲不一樣的類定義不一樣的render。當時這種設計也是合理的,由於對Throwable的渲染最主要的就是其棧的渲染,其餘的沒什麼大的不一樣,並且對棧渲染方式保持相同的格式會比較好。
1
private ThrowableRenderer throwableRenderer =
null;
2
public Hierarchy(Logger root) {
3
4 defaultFactory =
new DefaultCategoryFactory();
5
6 }
7
public
void setThrowableRenderer(
final ThrowableRenderer renderer) {
8 throwableRenderer = renderer;
9 }
10
public ThrowableRenderer getThrowableRenderer() {
11
return throwableRenderer;
12 }
Configurator接口
Configurator接口用於定義對配置文件的解析。在Log4J中配置文件解析出來的全部信息均可以放在LoggerRepository中,於是Configurator接口的定義很是簡單。
1
public
interface Configurator {
2
public
static
final String INHERITED = "inherited";
3
public
static
final String NULL = "null";
4
void doConfigure(URL url, LoggerRepository repository);
5 }
Log4J支持兩種文件形式的配置文件:properties文件和xml文件,他們風別對應PropertyConfigurator類和DOMConfigurator類。
PropertyConfigurator類
PropertyConfigurator類解析properties文件的中的配置信息,能夠設置log4j.debug爲true以打開Log4J內部的日誌信息;另外PropertyConfigurator還支持Linux風格的變量,即全部${variable}形式的變量都會被系統中對應的屬性或配置文件內部定義的屬性替換(先查找系統中的屬性,後查找配置文件內部定義的屬性);可是PropertyConfigurator不支持一些Log4J中的高級功能,如自定義ErrorHandler和定義AsyncAppender等。
Configurator中最重要的方法是doConfigure()方法,在PropertyConfigurator實現中,首先將配置文件對應的URL讀取成Properties對象:
1
public
void doConfigure(java.net.URL configURL, LoggerRepository hierarchy) {
2 Properties props =
new Properties();
3
4 uConn = configURL.openConnection();
5 uConn.setUseCaches(
false);
6 istream = uConn.getInputStream();
7 props.load(istream);
8
9 doConfigure(props, hierarchy);
10 }
然後檢查是否設置了log4j.debug、log4j.reset、log4j.threshold等屬性,若是有則作相應的設置。這裏經過OptionConverter.findAndSubst()方法實現屬性的查找和變量信息的替換。
1
public
void doConfigure(Properties properties, LoggerRepository hierarchy) {
2 repository = hierarchy;
3 String value = properties.getProperty(LogLog.DEBUG_KEY);
4
if (value ==
null) {
5 value = properties.getProperty("log4j.configDebug");
6
if (value !=
null)
7 LogLog.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");
8 }
9
if (value !=
null) {
10 LogLog.setInternalDebugging(OptionConverter.toBoolean(value,
true));
11 }
12 String reset = properties.getProperty(RESET_KEY);
13
if (reset !=
null && OptionConverter.toBoolean(reset,
false)) {
14 hierarchy.resetConfiguration();
15 }
16 String thresholdStr = OptionConverter.findAndSubst(THRESHOLD_PREFIX,
17 properties);
18
if (thresholdStr !=
null) {
19 hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr,
20 (Level) Level.ALL));
21 LogLog.debug("Hierarchy threshold set to ["
22 + hierarchy.getThreshold() + "].");
23 }
24
25 }
而後分三步解析配置信息:
1. 解析Root Logger配置
首先找到log4j.rootLogger的值,它以逗號’,’分隔,其中第一個值時root的Level信息,以後是要添加到root的Appender名字。對Level信息,直接設置給root就行。對Appender名字,繼續解析。
1
void parseCategory(Properties props, Logger logger, String optionKey,
2 String loggerName, String value) {
3 LogLog.debug("Parsing for [" + loggerName + "] with value=[" + value
4 + "].");
5 StringTokenizer st =
new StringTokenizer(value, ",");
6
if (!(value.startsWith(",") || value.equals(""))) {
7
if (!st.hasMoreTokens())
8
return;
9 String levelStr = st.nextToken();
10 LogLog.debug("Level token is [" + levelStr + "].");
11
if (INHERITED.equalsIgnoreCase(levelStr)
12 || NULL.equalsIgnoreCase(levelStr)) {
13
if (loggerName.equals(INTERNAL_ROOT_NAME)) {
14 LogLog.warn("The root logger cannot be set to null.");
15 }
else {
16 logger.setLevel(
null);
17 }
18 }
else {
19 logger.setLevel(OptionConverter.toLevel(levelStr,
20 (Level) Level.DEBUG));
21 }
22 LogLog.debug("Category " + loggerName + " set to "
23 + logger.getLevel());
24 }
25 logger.removeAllAppenders();
26 Appender appender;
27 String appenderName;
28
while (st.hasMoreTokens()) {
29 appenderName = st.nextToken().trim();
30
if (appenderName ==
null || appenderName.equals(","))
31
continue;
32 LogLog.debug("Parsing appender named \"" + appenderName + "\".");
33 appender = parseAppender(props, appenderName);
34
if (appender !=
null) {
35 logger.addAppender(appender);
36 }
37 }
38 }
相同的Appender能夠添加到不一樣的Logger中,於是PropertyConfigurator對Appender作了緩存,若是能從緩存中找到相應的Appender類,則直接返回找到的Appender。
然後解析如下鍵值名以及對應類的屬性信息:
log4j.appender.appenderName=…
log4j.appender.appenderName.layout=…
log4j.appender.appenderName.errorhandler=…
log4j.appender.appenderName.filter.filterKey.name=…
1 Appender parseAppender(Properties props, String appenderName) {
2 Appender appender = registryGet(appenderName);
3
if ((appender !=
null)) {
4 LogLog.debug("Appender \"" + appenderName
5 + "\" was already parsed.");
6
return appender;
7 }
8 String prefix = APPENDER_PREFIX + appenderName;
9 String layoutPrefix = prefix + ".layout";
10 appender = (Appender) OptionConverter.instantiateByKey(props, prefix,
11 org.apache.log4j.Appender.
class,
null);
12
if (appender ==
null) {
13 LogLog.error("Could not instantiate appender named \""
14 + appenderName + "\".");
15
return
null;
16 }
17 appender.setName(appenderName);
18
if (appender
instanceof OptionHandler) {
19
if (appender.requiresLayout()) {
20 Layout layout = (Layout) OptionConverter.instantiateByKey(
21 props, layoutPrefix, Layout.
class,
null);
22
if (layout !=
null) {
23 appender.setLayout(layout);
24 LogLog.debug("Parsing layout options for \"" + appenderName
25 + "\".");
26 PropertySetter.setProperties(layout, props, layoutPrefix
27 + ".");
28 LogLog.debug("End of parsing for \"" + appenderName + "\".");
29 }
30 }
31
final String errorHandlerPrefix = prefix + ".errorhandler";
32 String errorHandlerClass = OptionConverter.findAndSubst(
33 errorHandlerPrefix, props);
34
if (errorHandlerClass !=
null) {
35 ErrorHandler eh = (ErrorHandler) OptionConverter
36 .instantiateByKey(props, errorHandlerPrefix,
37 ErrorHandler.
class,
null);
38
if (eh !=
null) {
39 appender.setErrorHandler(eh);
40 LogLog.debug("Parsing errorhandler options for \""
41 + appenderName + "\".");
42 parseErrorHandler(eh, errorHandlerPrefix, props, repository);
43
final Properties edited =
new Properties();
44
final String[] keys =
new String[] {
45 errorHandlerPrefix + "." + ROOT_REF,
46 errorHandlerPrefix + "." + LOGGER_REF,
47 errorHandlerPrefix + "." + APPENDER_REF_TAG };
48
for (Iterator iter = props.entrySet().iterator(); iter
49 .hasNext();) {
50 Map.Entry entry = (Map.Entry) iter.next();
51
int i = 0;
52
for (; i < keys.length; i++) {
53
if (keys[i].equals(entry.getKey()))
54
break;
55 }
56
if (i == keys.length) {
57 edited.put(entry.getKey(), entry.getValue());
58 }
59 }
60 PropertySetter.setProperties(eh, edited, errorHandlerPrefix
61 + ".");
62 LogLog.debug("End of errorhandler parsing for \""
63 + appenderName + "\".");
64 }
65 }
66 PropertySetter.setProperties(appender, props, prefix + ".");
67 LogLog.debug("Parsed \"" + appenderName + "\" options.");
68 }
69 parseAppenderFilters(props, appenderName, appender);
70 registryPut(appender);
71
return appender;
72 }
2. 解析LoggerFactory配置
查找log4j.loggerFactory的值,保存建立的LoggerFactory實例,使用log4j.loggerFactory.propName的方式設置LoggerFactory實例的屬性。
1
protected
void configureLoggerFactory(Properties props) {
2 String factoryClassName = OptionConverter.findAndSubst(
3 LOGGER_FACTORY_KEY, props);
4
if (factoryClassName !=
null) {
5 LogLog.debug("Setting category factory to [" + factoryClassName
6 + "].");
7 loggerFactory = (LoggerFactory) OptionConverter
8 .instantiateByClassName(factoryClassName,
9 LoggerFactory.
class, loggerFactory);
10 PropertySetter.setProperties(loggerFactory, props, FACTORY_PREFIX
11 + ".");
12 }
13 }
3. 解析非Root Logger和ObjectRender配置
解析log4j.logger.、log4j.renderer.、log4j.throwableRenderer.等信息。
另外,PropertyConfigurator還經過PropertyWatchLog類支持每一個一段時間檢查一次,若是發現配置文件有改動,則自動從新加載配置信息。
DOMConfigurator類
DOMConfigurator使用DOM解析全部Log4J配置文件中的元素,並根據DOM中的元素查找對應的RootLogger、Logger、Appender、Layout等模塊。另外DOMConfigurator也支持每隔一段時間檢查文件是否有修改,如有,則從新載入新修改後的配置文件。這裏DOMConfigurator的實現方式和PropertyConfigurator的實現方式相似,再也不詳細介紹。
LogManager類
LogManager將Configurator和LoggerRepository整合在一塊兒,它在初始化的時候找到Log4J配置文件,而且將其解析到LoggerRepository中。
1
static
{
2
Hierarchy h
=
new
Hierarchy(
new
RootLogger((Level) Level.DEBUG));
3
repositorySelector
=
new
DefaultRepositorySelector(h);
4
String override
=
OptionConverter.getSystemProperty(
5
DEFAULT_INIT_OVERRIDE_KEY,
null
);
6
if
(override
==
null
||
"
false
"
.equalsIgnoreCase(override)) {
7
String configurationOptionStr
=
OptionConverter.getSystemProperty(
8
DEFAULT_CONFIGURATION_KEY,
null
);
9
String configuratorClassName
=
OptionConverter.getSystemProperty(
10
CONFIGURATOR_CLASS_KEY,
null
);
11
URL url
=
null
;
12
if
(configurationOptionStr
==
null
) {
13
url
=
Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
14
if
(url
==
null
) {
15
url
=
Loader.getResource(DEFAULT_CONFIGURATION_FILE);
16
}
17
}
else
{
18
try
{
19
url
=
new
URL(configurationOptionStr);
20
}
catch
(MalformedURLException ex) {
21
url
=
Loader.getResource(configurationOptionStr);
22
}
23
}
24
if
(url
!=
null
) {
25
LogLog.debug(
"
Using URL [
"
+
url
26
+
"
] for automatic log4j configuration.
"
);
27
try
{
28
OptionConverter.selectAndConfigure(url,
29
configuratorClassName,
30
LogManager.getLoggerRepository());
31
}
catch
(NoClassDefFoundError e) {
32
LogLog.warn(
"
Error during default initialization
"
, e);
33
}
34
}
else
{
35
LogLog.debug(
"
Could not find resource: [
"
36
+
configurationOptionStr
+
"
].
"
);
37
}
38
}
else
{
39
LogLog.debug(
"
Default initialization of overridden by
"
40
+
DEFAULT_INIT_OVERRIDE_KEY
+
"
property.
"
);
41
}
42
}