LogManager,获取Logger实例或者操作当前的LoggerRepository。
LogManager类的初始化过程在加载到内存前已经完成。
在使用log4j时, 我们一开始会获取Logger:
org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(this.getClass());
其中Logger.getLogger
/**
* Shorthand for <code>getLogger(clazz.getName())</code>.
*
* @param clazz The name of <code>clazz</code> will be used as the
* name of the logger to retrieve. See {@link #getLogger(String)}
* for more detailed information.
*/
static public Logger getLogger(Class clazz) {
return LogManager.getLogger(clazz.getName());
}
这里最主要工作在LogManager这个类里,
首先看static代码块:
static {
//这里使用默认的selector
Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));
repositorySelector = new DefaultRepositorySelector(h);
/** 查找log4j.properties */
//DEFAULT_INIT_OVERRIDE_KEY="log4j.defaultInitOverride"
String override = OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,
null);
// 若log4j.properties没被override,则获取默认配置
if (override == null || "false".equalsIgnoreCase(override)) {
//DEFAULT_CONFIGURATION_KEY="log4j.configuration"
String configurationOptionStr = OptionConverter.getSystemProperty(
DEFAULT_CONFIGURATION_KEY,
null);
//CONFIGURATOR_CLASS_KEY="log4j.configuratorClass"
String configuratorClassName = OptionConverter.getSystemProperty(
CONFIGURATOR_CLASS_KEY,
null);
URL url = null;
//用户没设置log4j.configuration,则加载log4j.xml,或者log4j.properties
if (configurationOptionStr == null) {
//DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml"
url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
if (url == null) {
//DEFAULT_CONFIGURATION_FILE = "log4j.properties"
url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
}
} else {
try {
url = new URL(configurationOptionStr);
} catch (MalformedURLException ex) {
// so, resource is not a URL:
// attempt to get the resource from the class path
url = Loader.getResource(configurationOptionStr);
}
}
// url不为空,剩下的工作交给OptionConverter.selectAndConfigure
if (url != null) {
LogLog.debug("Using URL [" + url + "] for automatic log4j configuration.");
try {
OptionConverter.selectAndConfigure(url, configuratorClassName,
LogManager.getLoggerRepository());
} catch (NoClassDefFoundError e) {
LogLog.warn("Error during default initialization", e);
}
} else {
LogLog.debug("Could not find resource: [" + configurationOptionStr + "].");
}
} else {
LogLog.debug("Default initialization of overridden by " +
DEFAULT_INIT_OVERRIDE_KEY + "property.");
}
}
LogManager中还有一些方法:
//设置RepositorySelector
static public void setRepositorySelector(RepositorySelector selector, Object guard)
throws IllegalArgumentException {
if((LogManager.guard != null) && (LogManager.guard != guard)) {
throw new IllegalArgumentException(
"Attempted to reset the LoggerFactory without possessing the guard.");
}
if(selector == null) {
throw new IllegalArgumentException("RepositorySelector must be non-null.");
}
LogManager.guard = guard;
LogManager.repositorySelector = selector;
}
//
static public LoggerRepository getLoggerRepository() {
if (repositorySelector == null) {
repositorySelector = new DefaultRepositorySelector(new NOPLoggerRepository());
guard = null;
Exception ex = new IllegalStateException("Class invariant violation");
String msg =
"log4j called after unloading, see http://logging.apache.org/log4j/1.2/faq.html#unload.";
if (isLikelySafeScenario(ex)) {
LogLog.debug(msg, ex);
} else {
LogLog.error(msg, ex);
}
}
return repositorySelector.getLoggerRepository();
}
public static Logger getRootLogger() {
return getLoggerRepository().getRootLogger();
}
public
static
Logger getLogger(final String name) {
// Delegate the actual manufacturing of the logger to the logger repository.
return getLoggerRepository().getLogger(name);
}
/**
Retrieve the appropriate {@link Logger} instance.
*/
public
static
Logger getLogger(final Class clazz) {
// Delegate the actual manufacturing of the logger to the logger repository.
return getLoggerRepository().getLogger(clazz.getName());
}
/**
Retrieve the appropriate {@link Logger} instance.
*/
public
static
Logger getLogger(final String name, final LoggerFactory factory) {
// Delegate the actual manufacturing of the logger to the logger repository.
return getLoggerRepository().getLogger(name, factory);
}
public
static
Logger exists(final String name) {
return getLoggerRepository().exists(name);
}
public
static
Enumeration getCurrentLoggers() {
return getLoggerRepository().getCurrentLoggers();
}
public
static
void shutdown() {
getLoggerRepository().shutdown();
}
public
static
void resetConfiguration() {
getLoggerRepository().resetConfiguration();
}
现在看OptionConverter.selectAndConfigure是如何加载配置信息的:
OptionConverter.selectAndConfigure(url, //配置文件的url
configuratorClassName,//配置的类名,一般是空的
LogManager.getLoggerRepository());//从repositorySelector获取LoggerRepository
上代码:
static public void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) {
Configurator configurator = null;
String filename = url.getFile();
if(clazz == null && filename != null && filename.endsWith(".xml")) {
clazz = "org.apache.log4j.xml.DOMConfigurator";
}
if(clazz != null) {
LogLog.debug("Preferred configurator class: " + clazz);
configurator = (Configurator) instantiateByClassName(clazz,
Configurator.class,
null);
if(configurator == null) {
LogLog.error("Could not instantiate configurator ["+clazz+"].");
return;
}
} else {
//如果没有一些自定义的配置,正常是代码是走这里
configurator = new PropertyConfigurator();
}
//然后读取配置文件
configurator.doConfigure(url, hierarchy);
}
再看PropertyConfigurator.doConfigure方法:
获取配置信息的代码省略
Properties props = new Properties();
//获取配置信息的代码省略
//
doConfigure(props, hierarchy);
对配置信息读取后进行的处理:
public void doConfigure(Properties properties, LoggerRepository hierarchy) {
repository = hierarchy;
String value = properties.getProperty(LogLog.DEBUG_KEY);
if(value == null) {
value = properties.getProperty("log4j.configDebug");
if(value != null)
LogLog.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");
}
if(value != null) {
LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true));
}
//
// if log4j.reset=true then
// reset hierarchy
String reset = properties.getProperty(RESET_KEY);
if (reset != null && OptionConverter.toBoolean(reset, false)) {
hierarchy.resetConfiguration();
}
String thresholdStr = OptionConverter.findAndSubst(THRESHOLD_PREFIX,
properties);
if(thresholdStr != null) {
hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr,
(Level) Level.ALL));
LogLog.debug("Hierarchy threshold set to ["+hierarchy.getThreshold()+"].");
}
//这里的三个方法,完成日志的配置
configureRootCategory(properties, hierarchy);
//配置属性log4j.loggerFactory,包括log4j.factory前缀的属性
configureLoggerFactory(properties);
//Parse non-root elements, such non-root categories and renderers.
//解析renderer或者categories,非root元素
parseCatsAndRenderers(properties, hierarchy);
LogLog.debug("Finished configuring.");
// We don't want to hold references to appenders preventing their
// garbage collection.
registry.clear();
}
先来看第一个方法都做了些什么工作:
void configureRootCategory(Properties props, LoggerRepository hierarchy) {
String effectiveFrefix = ROOT_LOGGER_PREFIX;
String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);
if(value == null) {
value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);
effectiveFrefix = ROOT_CATEGORY_PREFIX;
}
if(value == null)
LogLog.debug("Could not find root logger information. Is this OK?");
else {
Logger root = hierarchy.getRootLogger();
synchronized(root) {
parseCategory(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
}
}
}
从上面的代码可看出,log4j的配置文件,必须有属性log4j.rootLogger或者log4j.rootCategory。
当该属性的值不为空,调用parseCategory(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value)处理。
该方法做了如下工作:
1. log4j.rootLogger或者log4j.rootCategory的值可以用”,”分隔
2. 获取每个值,进行日志级别的设置。
3. 清除当前日志所有appenders
4. 解析Appender并返回,添加到logger中
void parseCategory(Properties props, Logger logger, String optionKey,
String loggerName, String value) {
LogLog.debug("Parsing for [" +loggerName +"] with value=[" + value+"].");
// We must skip over ',' but not white space
StringTokenizer st = new StringTokenizer(value, ",");
// If value is not in the form ", appender.." or "", then we should set
// the level of the loggeregory.
if(!(value.startsWith(",") || value.equals(""))) {
// just to be on the safe side...
if(!st.hasMoreTokens())
return;
String levelStr = st.nextToken();
LogLog.debug("Level token is [" + levelStr + "].");
// If the level value is inherited, set category level value to
// null. We also check that the user has not specified inherited for the
// root category.
if(INHERITED.equalsIgnoreCase(levelStr) ||
NULL.equalsIgnoreCase(levelStr)) {
if(loggerName.equals(INTERNAL_ROOT_NAME)) {
LogLog.warn("The root logger cannot be set to null.");
} else {
logger.setLevel(null);
}
} else {
logger.setLevel(OptionConverter.toLevel(levelStr, (Level) Level.DEBUG));
}
LogLog.debug("Category " + loggerName + " set to " + logger.getLevel());
}
// Begin by removing all existing appenders.
logger.removeAllAppenders();
Appender appender;
String appenderName;
while(st.hasMoreTokens()) {
appenderName = st.nextToken().trim();
if(appenderName == null || appenderName.equals(","))
continue;
LogLog.debug("Parsing appender named \"" + appenderName +"\".");
appender = parseAppender(props, appenderName);
if(appender != null) {
logger.addAppender(appender);
}
}
}
解析Appender并返回,代码如下:
Appender parseAppender(Properties props, String appenderName) {
//从Hashtable中获取
Appender appender = registryGet(appenderName);
if((appender != null)) {
LogLog.debug("Appender \"" + appenderName + "\" was already parsed.");
return appender;
}
// Appender was not previously initialized.
String prefix = APPENDER_PREFIX + appenderName;
String layoutPrefix = prefix + ".layout";
appender = (Appender) OptionConverter.instantiateByKey(props, prefix,
org.apache.log4j.Appender.class,
null);
if(appender == null) {
LogLog.error(
"Could not instantiate appender named \"" + appenderName+"\".");
return null;
}
appender.setName(appenderName);
if(appender instanceof OptionHandler) {
if(appender.requiresLayout()) {
Layout layout = (Layout) OptionConverter.instantiateByKey(props,
layoutPrefix,
Layout.class,
null);
if(layout != null) {
appender.setLayout(layout);
LogLog.debug("Parsing layout options for \"" + appenderName +"\".");
//configureOptionHandler(layout, layoutPrefix + ".", props);
PropertySetter.setProperties(layout, props, layoutPrefix + ".");
LogLog.debug("End of parsing for \"" + appenderName +"\".");
}
}
final String errorHandlerPrefix = prefix + ".errorhandler";
String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props);
if (errorHandlerClass != null) {
ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByKey(props,
errorHandlerPrefix,
ErrorHandler.class,
null);
//如果对错误有设置处理事件,遍历
if (eh != null) {
appender.setErrorHandler(eh);
LogLog.debug("Parsing errorhandler options for \"" + appenderName +"\".");
parseErrorHandler(eh, errorHandlerPrefix, props, repository);
final Properties edited = new Properties();
final String[] keys = new String[] {
errorHandlerPrefix + "." + ROOT_REF,
errorHandlerPrefix + "." + LOGGER_REF,
errorHandlerPrefix + "." + APPENDER_REF_TAG
};
for(Iterator iter = props.entrySet().iterator();iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
int i = 0;
for(; i < keys.length; i++) {
if(keys[i].equals(entry.getKey())) break;
}
if (i == keys.length) {
edited.put(entry.getKey(), entry.getValue());
}
}
PropertySetter.setProperties(eh, edited, errorHandlerPrefix + ".");
LogLog.debug("End of errorhandler parsing for \"" + appenderName +"\".");
}
}
PropertySetter.setProperties(appender, props, prefix + ".");
LogLog.debug("Parsed \"" + appenderName +"\" options.");
}
//这里过滤Appender信息,或有设置,按照设置过滤处理日志信息,然后输出到日志文件或控制台中。
parseAppenderFilters(props, appenderName, appender);
//这里将生成的appender放入Hashtable,以便于下次获取,不用重新生成
registryPut(appender);
return appender;
}