org.apache.log4j.xml.DOMConfigurator Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.log4j.xml;
import org.apache.log4j.Appender;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.config.PropertySetter;
import org.apache.log4j.helpers.FileWatchdog;
import org.apache.log4j.helpers.Loader;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.or.RendererMap;
import org.apache.log4j.spi.AppenderAttachable;
import org.apache.log4j.spi.Configurator;
import org.apache.log4j.spi.ErrorHandler;
import org.apache.log4j.spi.Filter;
import org.apache.log4j.spi.LoggerFactory;
import org.apache.log4j.spi.LoggerRepository;
import org.apache.log4j.spi.RendererSupport;
import org.apache.log4j.spi.ThrowableRenderer;
import org.apache.log4j.spi.ThrowableRendererSupport;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.Reader;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Hashtable;
import java.util.Properties;
// Contributors: Mark Womack
// Arun Katkere
/**
Use this class to initialize the log4j environment using a DOM tree.
The DTD is specified in log4j.dtd.
Sometimes it is useful to see how log4j is reading configuration
files. You can enable log4j internal logging by defining the
log4j.debug variable on the java command
line. Alternatively, set the debug
attribute in the
log4j:configuration
element. As in
<log4j:configuration debug="true" xmlns:log4j="http://jakarta.apache.org/log4j/">
...
</log4j:configuration>
There are sample XML files included in the package.
@author Christopher Taylor
@author Ceki Gülcü
@author Anders Kristensen
@since 0.8.3 */
public class DOMConfigurator implements Configurator {
static final String CONFIGURATION_TAG = "log4j:configuration";
static final String OLD_CONFIGURATION_TAG = "configuration";
static final String RENDERER_TAG = "renderer";
private static final String THROWABLE_RENDERER_TAG = "throwableRenderer";
static final String APPENDER_TAG = "appender";
static final String APPENDER_REF_TAG = "appender-ref";
static final String PARAM_TAG = "param";
static final String LAYOUT_TAG = "layout";
static final String CATEGORY = "category";
static final String LOGGER = "logger";
static final String LOGGER_REF = "logger-ref";
static final String CATEGORY_FACTORY_TAG = "categoryFactory";
static final String LOGGER_FACTORY_TAG = "loggerFactory";
static final String NAME_ATTR = "name";
static final String CLASS_ATTR = "class";
static final String VALUE_ATTR = "value";
static final String ROOT_TAG = "root";
static final String ROOT_REF = "root-ref";
static final String LEVEL_TAG = "level";
static final String PRIORITY_TAG = "priority";
static final String FILTER_TAG = "filter";
static final String ERROR_HANDLER_TAG = "errorHandler";
static final String REF_ATTR = "ref";
static final String ADDITIVITY_ATTR = "additivity";
static final String THRESHOLD_ATTR = "threshold";
static final String CONFIG_DEBUG_ATTR = "configDebug";
static final String INTERNAL_DEBUG_ATTR = "debug";
private static final String RESET_ATTR = "reset";
static final String RENDERING_CLASS_ATTR = "renderingClass";
static final String RENDERED_CLASS_ATTR = "renderedClass";
static final String EMPTY_STR = "";
static final Class[] ONE_STRING_PARAM = new Class[] {String.class};
final static String dbfKey = "javax.xml.parsers.DocumentBuilderFactory";
// key: appenderName, value: appender
Hashtable appenderBag;
Properties props;
LoggerRepository repository;
protected LoggerFactory catFactory = null;
/**
No argument constructor.
*/
public
DOMConfigurator () {
appenderBag = new Hashtable();
}
/**
Used internally to parse appenders by IDREF name.
*/
protected
Appender findAppenderByName(Document doc, String appenderName) {
Appender appender = (Appender) appenderBag.get(appenderName);
if(appender != null) {
return appender;
} else {
// Doesn't work on DOM Level 1 :
// Element element = doc.getElementById(appenderName);
// Endre's hack:
Element element = null;
NodeList list = doc.getElementsByTagName("appender");
for (int t=0; t < list.getLength(); t++) {
Node node = list.item(t);
NamedNodeMap map= node.getAttributes();
Node attrNode = map.getNamedItem("name");
if (appenderName.equals(attrNode.getNodeValue())) {
element = (Element) node;
break;
}
}
// Hack finished.
if(element == null) {
LogLog.error("No appender named ["+appenderName+"] could be found.");
return null;
} else {
appender = parseAppender(element);
if (appender != null) {
appenderBag.put(appenderName, appender);
}
return appender;
}
}
}
/**
Used internally to parse appenders by IDREF element.
*/
protected
Appender findAppenderByReference(Element appenderRef) {
String appenderName = subst(appenderRef.getAttribute(REF_ATTR));
Document doc = appenderRef.getOwnerDocument();
return findAppenderByName(doc, appenderName);
}
/**
* Delegates unrecognized content to created instance if
* it supports UnrecognizedElementParser.
* @since 1.2.15
* @param instance instance, may be null.
* @param element element, may not be null.
* @param props properties
* @throws IOException thrown if configuration of owner object
* should be abandoned.
*/
private static void parseUnrecognizedElement(final Object instance,
final Element element,
final Properties props) throws Exception {
boolean recognized = false;
if (instance instanceof UnrecognizedElementHandler) {
recognized = ((UnrecognizedElementHandler) instance).parseUnrecognizedElement(
element, props);
}
if (!recognized) {
LogLog.warn("Unrecognized element " + element.getNodeName());
}
}
/**
* Delegates unrecognized content to created instance if
* it supports UnrecognizedElementParser and catches and
* logs any exception.
* @since 1.2.15
* @param instance instance, may be null.
* @param element element, may not be null.
* @param props properties
*/
private static void quietParseUnrecognizedElement(final Object instance,
final Element element,
final Properties props) {
try {
parseUnrecognizedElement(instance, element, props);
} catch (Exception ex) {
if (ex instanceof InterruptedException || ex instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
LogLog.error("Error in extension content: ", ex);
}
}
/**
Used internally to parse an appender element.
*/
protected
Appender parseAppender (Element appenderElement) {
String className = subst(appenderElement.getAttribute(CLASS_ATTR));
LogLog.debug("Class name: [" + className+']');
try {
Object instance = Loader.loadClass(className).newInstance();
Appender appender = (Appender)instance;
PropertySetter propSetter = new PropertySetter(appender);
appender.setName(subst(appenderElement.getAttribute(NAME_ATTR)));
NodeList children = appenderElement.getChildNodes();
final int length = children.getLength();
for (int loop = 0; loop < length; loop++) {
Node currentNode = children.item(loop);
/* We're only interested in Elements */
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
Element currentElement = (Element)currentNode;
// Parse appender parameters
if (currentElement.getTagName().equals(PARAM_TAG)) {
setParameter(currentElement, propSetter);
}
// Set appender layout
else if (currentElement.getTagName().equals(LAYOUT_TAG)) {
appender.setLayout(parseLayout(currentElement));
}
// Add filters
else if (currentElement.getTagName().equals(FILTER_TAG)) {
parseFilters(currentElement, appender);
}
else if (currentElement.getTagName().equals(ERROR_HANDLER_TAG)) {
parseErrorHandler(currentElement, appender);
}
else if (currentElement.getTagName().equals(APPENDER_REF_TAG)) {
String refName = subst(currentElement.getAttribute(REF_ATTR));
if(appender instanceof AppenderAttachable) {
AppenderAttachable aa = (AppenderAttachable) appender;
LogLog.debug("Attaching appender named ["+ refName+
"] to appender named ["+ appender.getName()+"].");
aa.addAppender(findAppenderByReference(currentElement));
} else {
LogLog.error("Requesting attachment of appender named ["+
refName+ "] to appender named ["+ appender.getName()+
"] which does not implement org.apache.log4j.spi.AppenderAttachable.");
}
} else {
parseUnrecognizedElement(instance, currentElement, props);
}
}
}
propSetter.activate();
return appender;
}
/* Yes, it's ugly. But all of these exceptions point to the same
problem: we can't create an Appender */
catch (Exception oops) {
if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
LogLog.error("Could not create an Appender. Reported error follows.",
oops);
return null;
}
}
/**
Used internally to parse an {@link ErrorHandler} element.
*/
protected
void parseErrorHandler(Element element, Appender appender) {
ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName(
subst(element.getAttribute(CLASS_ATTR)),
org.apache.log4j.spi.ErrorHandler.class,
null);
if(eh != null) {
eh.setAppender(appender);
PropertySetter propSetter = new PropertySetter(eh);
NodeList children = element.getChildNodes();
final int length = children.getLength();
for (int loop = 0; loop < length; loop++) {
Node currentNode = children.item(loop);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
Element currentElement = (Element) currentNode;
String tagName = currentElement.getTagName();
if(tagName.equals(PARAM_TAG)) {
setParameter(currentElement, propSetter);
} else if(tagName.equals(APPENDER_REF_TAG)) {
eh.setBackupAppender(findAppenderByReference(currentElement));
} else if(tagName.equals(LOGGER_REF)) {
String loggerName = currentElement.getAttribute(REF_ATTR);
Logger logger = (catFactory == null) ? repository.getLogger(loggerName)
: repository.getLogger(loggerName, catFactory);
eh.setLogger(logger);
} else if(tagName.equals(ROOT_REF)) {
Logger root = repository.getRootLogger();
eh.setLogger(root);
} else {
quietParseUnrecognizedElement(eh, currentElement, props);
}
}
}
propSetter.activate();
appender.setErrorHandler(eh);
}
}
/**
Used internally to parse a filter element.
*/
protected
void parseFilters(Element element, Appender appender) {
String clazz = subst(element.getAttribute(CLASS_ATTR));
Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz,
Filter.class, null);
if(filter != null) {
PropertySetter propSetter = new PropertySetter(filter);
NodeList children = element.getChildNodes();
final int length = children.getLength();
for (int loop = 0; loop < length; loop++) {
Node currentNode = children.item(loop);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
Element currentElement = (Element) currentNode;
String tagName = currentElement.getTagName();
if(tagName.equals(PARAM_TAG)) {
setParameter(currentElement, propSetter);
} else {
quietParseUnrecognizedElement(filter, currentElement, props);
}
}
}
propSetter.activate();
LogLog.debug("Adding filter of type ["+filter.getClass()
+"] to appender named ["+appender.getName()+"].");
appender.addFilter(filter);
}
}
/**
Used internally to parse an category element.
*/
protected
void parseCategory (Element loggerElement) {
// Create a new org.apache.log4j.Category object from the element.
String catName = subst(loggerElement.getAttribute(NAME_ATTR));
Logger cat;
String className = subst(loggerElement.getAttribute(CLASS_ATTR));
if(EMPTY_STR.equals(className)) {
LogLog.debug("Retreiving an instance of org.apache.log4j.Logger.");
cat = (catFactory == null) ? repository.getLogger(catName) : repository.getLogger(catName, catFactory);
}
else {
LogLog.debug("Desired logger sub-class: ["+className+']');
try {
Class clazz = Loader.loadClass(className);
Method getInstanceMethod = clazz.getMethod("getLogger",
ONE_STRING_PARAM);
cat = (Logger) getInstanceMethod.invoke(null, new Object[] {catName});
} catch (InvocationTargetException oops) {
if (oops.getTargetException() instanceof InterruptedException
|| oops.getTargetException() instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
LogLog.error("Could not retrieve category ["+catName+
"]. Reported error follows.", oops);
return;
} catch (Exception oops) {
LogLog.error("Could not retrieve category ["+catName+
"]. Reported error follows.", oops);
return;
}
}
// Setting up a category needs to be an atomic operation, in order
// to protect potential log operations while category
// configuration is in progress.
synchronized(cat) {
boolean additivity = OptionConverter.toBoolean(
subst(loggerElement.getAttribute(ADDITIVITY_ATTR)),
true);
LogLog.debug("Setting ["+cat.getName()+"] additivity to ["+additivity+"].");
cat.setAdditivity(additivity);
parseChildrenOfLoggerElement(loggerElement, cat, false);
}
}
/**
Used internally to parse the category factory element.
*/
protected
void parseCategoryFactory(Element factoryElement) {
String className = subst(factoryElement.getAttribute(CLASS_ATTR));
if(EMPTY_STR.equals(className)) {
LogLog.error("Category Factory tag " + CLASS_ATTR + " attribute not found.");
LogLog.debug("No Category Factory configured.");
}
else {
LogLog.debug("Desired category factory: ["+className+']');
Object factory = OptionConverter.instantiateByClassName(className,
LoggerFactory.class,
null);
if (factory instanceof LoggerFactory) {
catFactory = (LoggerFactory) factory;
} else {
LogLog.error("Category Factory class " + className + " does not implement org.apache.log4j.LoggerFactory");
}
PropertySetter propSetter = new PropertySetter(factory);
Element currentElement = null;
Node currentNode = null;
NodeList children = factoryElement.getChildNodes();
final int length = children.getLength();
for (int loop=0; loop < length; loop++) {
currentNode = children.item(loop);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
currentElement = (Element)currentNode;
if (currentElement.getTagName().equals(PARAM_TAG)) {
setParameter(currentElement, propSetter);
} else {
quietParseUnrecognizedElement(factory, currentElement, props);
}
}
}
}
}
/**
Used internally to parse the roor category element.
*/
protected
void parseRoot (Element rootElement) {
Logger root = repository.getRootLogger();
// category configuration needs to be atomic
synchronized(root) {
parseChildrenOfLoggerElement(rootElement, root, true);
}
}
/**
Used internally to parse the children of a category element.
*/
protected
void parseChildrenOfLoggerElement(Element catElement,
Logger cat, boolean isRoot) {
PropertySetter propSetter = new PropertySetter(cat);
// Remove all existing appenders from cat. They will be
// reconstructed if need be.
cat.removeAllAppenders();
NodeList children = catElement.getChildNodes();
final int length = children.getLength();
for (int loop = 0; loop < length; loop++) {
Node currentNode = children.item(loop);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
Element currentElement = (Element) currentNode;
String tagName = currentElement.getTagName();
if (tagName.equals(APPENDER_REF_TAG)) {
Element appenderRef = (Element) currentNode;
Appender appender = findAppenderByReference(appenderRef);
String refName = subst(appenderRef.getAttribute(REF_ATTR));
if(appender != null)
LogLog.debug("Adding appender named ["+ refName+
"] to category ["+cat.getName()+"].");
else
LogLog.debug("Appender named ["+ refName + "] not found.");
cat.addAppender(appender);
} else if(tagName.equals(LEVEL_TAG)) {
parseLevel(currentElement, cat, isRoot);
} else if(tagName.equals(PRIORITY_TAG)) {
parseLevel(currentElement, cat, isRoot);
} else if(tagName.equals(PARAM_TAG)) {
setParameter(currentElement, propSetter);
} else {
quietParseUnrecognizedElement(cat, currentElement, props);
}
}
}
propSetter.activate();
}
/**
Used internally to parse a layout element.
*/
protected
Layout parseLayout (Element layout_element) {
String className = subst(layout_element.getAttribute(CLASS_ATTR));
LogLog.debug("Parsing layout of class: \""+className+"\"");
try {
Object instance = Loader.loadClass(className).newInstance();
Layout layout = (Layout)instance;
PropertySetter propSetter = new PropertySetter(layout);
NodeList params = layout_element.getChildNodes();
final int length = params.getLength();
for (int loop = 0; loop < length; loop++) {
Node currentNode = (Node)params.item(loop);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
Element currentElement = (Element) currentNode;
String tagName = currentElement.getTagName();
if(tagName.equals(PARAM_TAG)) {
setParameter(currentElement, propSetter);
} else {
parseUnrecognizedElement(instance, currentElement, props);
}
}
}
propSetter.activate();
return layout;
}
catch (Exception oops) {
if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
LogLog.error("Could not create the Layout. Reported error follows.",
oops);
return null;
}
}
protected
void parseRenderer(Element element) {
String renderingClass = subst(element.getAttribute(RENDERING_CLASS_ATTR));
String renderedClass = subst(element.getAttribute(RENDERED_CLASS_ATTR));
if(repository instanceof RendererSupport) {
RendererMap.addRenderer((RendererSupport) repository, renderedClass,
renderingClass);
}
}
/**
* Parses throwable renderer.
* @param element throwableRenderer element.
* @return configured throwable renderer.
* @since 1.2.16.
*/
protected ThrowableRenderer parseThrowableRenderer(final Element element) {
String className = subst(element.getAttribute(CLASS_ATTR));
LogLog.debug("Parsing throwableRenderer of class: \""+className+"\"");
try {
Object instance = Loader.loadClass(className).newInstance();
ThrowableRenderer tr = (ThrowableRenderer)instance;
PropertySetter propSetter = new PropertySetter(tr);
NodeList params = element.getChildNodes();
final int length = params.getLength();
for (int loop = 0; loop < length; loop++) {
Node currentNode = (Node)params.item(loop);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
Element currentElement = (Element) currentNode;
String tagName = currentElement.getTagName();
if(tagName.equals(PARAM_TAG)) {
setParameter(currentElement, propSetter);
} else {
parseUnrecognizedElement(instance, currentElement, props);
}
}
}
propSetter.activate();
return tr;
}
catch (Exception oops) {
if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
LogLog.error("Could not create the ThrowableRenderer. Reported error follows.",
oops);
return null;
}
}
/**
Used internally to parse a level element.
*/
protected
void parseLevel(Element element, Logger logger, boolean isRoot) {
String catName = logger.getName();
if(isRoot) {
catName = "root";
}
String priStr = subst(element.getAttribute(VALUE_ATTR));
LogLog.debug("Level value for "+catName+" is ["+priStr+"].");
if(INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) {
if(isRoot) {
LogLog.error("Root level cannot be inherited. Ignoring directive.");
} else {
logger.setLevel(null);
}
} else {
String className = subst(element.getAttribute(CLASS_ATTR));
if(EMPTY_STR.equals(className)) {
logger.setLevel(OptionConverter.toLevel(priStr, Level.DEBUG));
} else {
LogLog.debug("Desired Level sub-class: ["+className+']');
try {
Class clazz = Loader.loadClass(className);
Method toLevelMethod = clazz.getMethod("toLevel",
ONE_STRING_PARAM);
Level pri = (Level) toLevelMethod.invoke(null,
new Object[] {priStr});
logger.setLevel(pri);
} catch (Exception oops) {
if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
LogLog.error("Could not create level ["+priStr+
"]. Reported error follows.", oops);
return;
}
}
}
LogLog.debug(catName + " level set to " + logger.getLevel());
}
protected
void setParameter(Element elem, PropertySetter propSetter) {
String name = subst(elem.getAttribute(NAME_ATTR));
String value = (elem.getAttribute(VALUE_ATTR));
value = subst(OptionConverter.convertSpecialChars(value));
propSetter.setProperty(name, value);
}
/**
Configure log4j using a configuration
element as
defined in the log4j.dtd.
*/
static
public
void configure (Element element) {
DOMConfigurator configurator = new DOMConfigurator();
configurator.doConfigure(element, LogManager.getLoggerRepository());
}
/**
Like {@link #configureAndWatch(String, long)} except that the
default delay as defined by {@link FileWatchdog#DEFAULT_DELAY} is
used.
@param configFilename A log4j configuration file in XML format.
*/
static
public
void configureAndWatch(String configFilename) {
configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY);
}
/**
Read the configuration file configFilename
if it
exists. Moreover, a thread will be created that will periodically
check if configFilename
has been created or
modified. The period is determined by the delay
argument. If a change or file creation is detected, then
configFilename
is read to configure log4j.
@param configFilename A log4j configuration file in XML format.
@param delay The delay in milliseconds to wait between each check.
*/
static
public
void configureAndWatch(String configFilename, long delay) {
XMLWatchdog xdog = new XMLWatchdog(configFilename);
xdog.setDelay(delay);
xdog.start();
}
private interface ParseAction {
Document parse(final DocumentBuilder parser) throws SAXException, IOException;
}
public
void doConfigure(final String filename, LoggerRepository repository) {
ParseAction action = new ParseAction() {
public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
return parser.parse(new File(filename));
}
public String toString() {
return "file [" + filename + "]";
}
};
doConfigure(action, repository);
}
public
void doConfigure(final URL url, LoggerRepository repository) {
ParseAction action = new ParseAction() {
public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
URLConnection uConn = url.openConnection();
uConn.setUseCaches(false);
InputStream stream = uConn.getInputStream();
try {
InputSource src = new InputSource(stream);
src.setSystemId(url.toString());
return parser.parse(src);
} finally {
stream.close();
}
}
public String toString() {
return "url [" + url.toString() + "]";
}
};
doConfigure(action, repository);
}
/**
Configure log4j by reading in a log4j.dtd compliant XML
configuration file.
*/
public
void doConfigure(final InputStream inputStream, LoggerRepository repository)
throws FactoryConfigurationError {
ParseAction action = new ParseAction() {
public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
InputSource inputSource = new InputSource(inputStream);
inputSource.setSystemId("dummy://log4j.dtd");
return parser.parse(inputSource);
}
public String toString() {
return "input stream [" + inputStream.toString() + "]";
}
};
doConfigure(action, repository);
}
/**
Configure log4j by reading in a log4j.dtd compliant XML
configuration file.
*/
public
void doConfigure(final Reader reader, LoggerRepository repository)
throws FactoryConfigurationError {
ParseAction action = new ParseAction() {
public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
InputSource inputSource = new InputSource(reader);
inputSource.setSystemId("dummy://log4j.dtd");
return parser.parse(inputSource);
}
public String toString() {
return "reader [" + reader.toString() + "]";
}
};
doConfigure(action, repository);
}
/**
Configure log4j by reading in a log4j.dtd compliant XML
configuration file.
*/
protected
void doConfigure(final InputSource inputSource, LoggerRepository repository)
throws FactoryConfigurationError {
if (inputSource.getSystemId() == null) {
inputSource.setSystemId("dummy://log4j.dtd");
}
ParseAction action = new ParseAction() {
public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
return parser.parse(inputSource);
}
public String toString() {
return "input source [" + inputSource.toString() + "]";
}
};
doConfigure(action, repository);
}
private final void doConfigure(final ParseAction action, final LoggerRepository repository)
throws FactoryConfigurationError {
DocumentBuilderFactory dbf = null;
this.repository = repository;
try {
LogLog.debug("System property is :"+
OptionConverter.getSystemProperty(dbfKey,
null));
dbf = DocumentBuilderFactory.newInstance();
LogLog.debug("Standard DocumentBuilderFactory search succeded.");
LogLog.debug("DocumentBuilderFactory is: "+dbf.getClass().getName());
} catch(FactoryConfigurationError fce) {
Exception e = fce.getException();
LogLog.debug("Could not instantiate a DocumentBuilderFactory.", e);
throw fce;
}
try {
dbf.setValidating(true);
DocumentBuilder docBuilder = dbf.newDocumentBuilder();
docBuilder.setErrorHandler(new SAXErrorHandler());
docBuilder.setEntityResolver(new Log4jEntityResolver());
Document doc = action.parse(docBuilder);
parse(doc.getDocumentElement());
} catch (Exception e) {
if (e instanceof InterruptedException || e instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
// I know this is miserable...
LogLog.error("Could not parse "+ action.toString() + ".", e);
}
}
/**
Configure by taking in an DOM element.
*/
public void doConfigure(Element element, LoggerRepository repository) {
this.repository = repository;
parse(element);
}
/**
A static version of {@link #doConfigure(String, LoggerRepository)}. */
static
public
void configure(String filename) throws FactoryConfigurationError {
new DOMConfigurator().doConfigure(filename,
LogManager.getLoggerRepository());
}
/**
A static version of {@link #doConfigure(URL, LoggerRepository)}.
*/
static
public
void configure(URL url) throws FactoryConfigurationError {
new DOMConfigurator().doConfigure(url, LogManager.getLoggerRepository());
}
/**
Used internally to configure the log4j framework by parsing a DOM
tree of XML elements based on log4j.dtd.
*/
protected
void parse(Element element) {
String rootElementName = element.getTagName();
if (!rootElementName.equals(CONFIGURATION_TAG)) {
if(rootElementName.equals(OLD_CONFIGURATION_TAG)) {
LogLog.warn("The <"+OLD_CONFIGURATION_TAG+
"> element has been deprecated.");
LogLog.warn("Use the <"+CONFIGURATION_TAG+"> element instead.");
} else {
LogLog.error("DOM element is - not a <"+CONFIGURATION_TAG+"> element.");
return;
}
}
String debugAttrib = subst(element.getAttribute(INTERNAL_DEBUG_ATTR));
LogLog.debug("debug attribute= \"" + debugAttrib +"\".");
// if the log4j.dtd is not specified in the XML file, then the
// "debug" attribute is returned as the empty string.
if(!debugAttrib.equals("") && !debugAttrib.equals("null")) {
LogLog.setInternalDebugging(OptionConverter.toBoolean(debugAttrib, true));
} else {
LogLog.debug("Ignoring " + INTERNAL_DEBUG_ATTR + " attribute.");
}
//
// reset repository before configuration if reset="true"
// on configuration element.
//
String resetAttrib = subst(element.getAttribute(RESET_ATTR));
LogLog.debug("reset attribute= \"" + resetAttrib +"\".");
if(!("".equals(resetAttrib))) {
if (OptionConverter.toBoolean(resetAttrib, false)) {
repository.resetConfiguration();
}
}
String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR));
if(!confDebug.equals("") && !confDebug.equals("null")) {
LogLog.warn("The \""+CONFIG_DEBUG_ATTR+"\" attribute is deprecated.");
LogLog.warn("Use the \""+INTERNAL_DEBUG_ATTR+"\" attribute instead.");
LogLog.setInternalDebugging(OptionConverter.toBoolean(confDebug, true));
}
String thresholdStr = subst(element.getAttribute(THRESHOLD_ATTR));
LogLog.debug("Threshold =\"" + thresholdStr +"\".");
if(!"".equals(thresholdStr) && !"null".equals(thresholdStr)) {
repository.setThreshold(thresholdStr);
}
//Hashtable appenderBag = new Hashtable(11);
/* Building Appender objects, placing them in a local namespace
for future reference */
// First configure each category factory under the root element.
// Category factories need to be configured before any of
// categories they support.
//
String tagName = null;
Element currentElement = null;
Node currentNode = null;
NodeList children = element.getChildNodes();
final int length = children.getLength();
for (int loop = 0; loop < length; loop++) {
currentNode = children.item(loop);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
currentElement = (Element) currentNode;
tagName = currentElement.getTagName();
if (tagName.equals(CATEGORY_FACTORY_TAG) || tagName.equals(LOGGER_FACTORY_TAG)) {
parseCategoryFactory(currentElement);
}
}
}
for (int loop = 0; loop < length; loop++) {
currentNode = children.item(loop);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
currentElement = (Element) currentNode;
tagName = currentElement.getTagName();
if (tagName.equals(CATEGORY) || tagName.equals(LOGGER)) {
parseCategory(currentElement);
} else if (tagName.equals(ROOT_TAG)) {
parseRoot(currentElement);
} else if(tagName.equals(RENDERER_TAG)) {
parseRenderer(currentElement);
} else if(tagName.equals(THROWABLE_RENDERER_TAG)) {
if (repository instanceof ThrowableRendererSupport) {
ThrowableRenderer tr = parseThrowableRenderer(currentElement);
if (tr != null) {
((ThrowableRendererSupport) repository).setThrowableRenderer(tr);
}
}
} else if (!(tagName.equals(APPENDER_TAG)
|| tagName.equals(CATEGORY_FACTORY_TAG)
|| tagName.equals(LOGGER_FACTORY_TAG))) {
quietParseUnrecognizedElement(repository, currentElement, props);
}
}
}
}
protected
String subst(final String value) {
return subst(value, props);
}
/**
* Substitutes property value for any references in expression.
*
* @param value value from configuration file, may contain
* literal text, property references or both
* @param props properties.
* @return evaluated expression, may still contain expressions
* if unable to expand.
* @since 1.2.15
*/
public static String subst(final String value, final Properties props) {
try {
return OptionConverter.substVars(value, props);
} catch (IllegalArgumentException e) {
LogLog.warn("Could not perform variable substitution.", e);
return value;
}
}
/**
* Sets a parameter based from configuration file content.
*
* @param elem param element, may not be null.
* @param propSetter property setter, may not be null.
* @param props properties
* @since 1.2.15
*/
public static void setParameter(final Element elem,
final PropertySetter propSetter,
final Properties props) {
String name = subst(elem.getAttribute("name"), props);
String value = (elem.getAttribute("value"));
value = subst(OptionConverter.convertSpecialChars(value), props);
propSetter.setProperty(name, value);
}
/**
* Creates an object and processes any nested param elements
* but does not call activateOptions. If the class also supports
* UnrecognizedElementParser, the parseUnrecognizedElement method
* will be call for any child elements other than param.
*
* @param element element, may not be null.
* @param props properties
* @param expectedClass interface or class expected to be implemented
* by created class
* @return created class or null.
* @throws Exception thrown if the contain object should be abandoned.
* @since 1.2.15
*/
public static Object parseElement(final Element element,
final Properties props,
final Class expectedClass) throws Exception {
String clazz = subst(element.getAttribute("class"), props);
Object instance = OptionConverter.instantiateByClassName(clazz,
expectedClass, null);
if (instance != null) {
PropertySetter propSetter = new PropertySetter(instance);
NodeList children = element.getChildNodes();
final int length = children.getLength();
for (int loop = 0; loop < length; loop++) {
Node currentNode = children.item(loop);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
Element currentElement = (Element) currentNode;
String tagName = currentElement.getTagName();
if (tagName.equals("param")) {
setParameter(currentElement, propSetter, props);
} else {
parseUnrecognizedElement(instance, currentElement, props);
}
}
}
return instance;
}
return null;
}
}
class XMLWatchdog extends FileWatchdog {
XMLWatchdog(String filename) {
super(filename);
}
/**
Call {@link DOMConfigurator#configure(String)} with the
filename
to reconfigure log4j. */
public
void doOnChange() {
new DOMConfigurator().doConfigure(filename,
LogManager.getLoggerRepository());
}
}