All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.log4j.xml.DOMConfigurator Maven / Gradle / Ivy

There is a newer version: 6.1.4
Show newest version
/*
 * 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. * * @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. * @since 1.2.15 */ 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. * * @param instance instance, may be null. * @param element element, may not be null. * @param props properties * @since 1.2.15 */ 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); // prevent XXE attacks dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); 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(); } LogLog.error("Could not parse " + action.toString() + ".", e); } catch (AbstractMethodError e) { LogLog.error("Failed to parse XML file. Missing DocumentBuilderFactory.setFeature() method?", e); throw 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()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy