| /* |
| * 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.plugins; |
| |
| 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.PropertyConfigurator; |
| 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.OptionHandler; |
| import org.apache.log4j.spi.RendererSupport; |
| import org.apache.log4j.xml.DOMConfigurator; |
| import org.apache.log4j.xml.SAXErrorHandler; |
| import org.apache.log4j.xml.UnrecognizedElementHandler; |
| 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.EntityResolver; |
| 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.Reader; |
| import java.lang.reflect.Method; |
| import java.net.URL; |
| import java.util.Hashtable; |
| import java.util.Properties; |
| |
| /** |
| * This is a duplicate (with minor modifications) |
| * of the log4j 1.2.15 DOMConfigurator |
| * renamed for use with earlier versions of log4j. |
| * |
| */ |
| public class PluginConfigurator implements Configurator { |
| |
| static final String CONFIGURATION_TAG = "log4j:configuration"; |
| static final String OLD_CONFIGURATION_TAG = "configuration"; |
| static final String RENDERER_TAG = "renderer"; |
| 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"; |
| 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 |
| private Hashtable appenderBag; |
| |
| private Properties props; |
| private LoggerRepository repository; |
| |
| private LoggerFactory catFactory = null; |
| |
| /** |
| No argument constructor. |
| */ |
| public |
| PluginConfigurator () { |
| 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); |
| 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) { |
| 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, appenderElement, 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) { |
| 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, element, 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, element, 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 <category> 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 (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, factoryElement, 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, catElement, 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, layout_element, props); |
| } |
| } |
| } |
| |
| propSetter.activate(); |
| return layout; |
| } |
| catch (Exception oops) { |
| 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); |
| } |
| } |
| |
| /** |
| 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) { |
| 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) { |
| setParameter(elem, propSetter, props); |
| } |
| |
| |
| /** |
| Configure log4j using a <code>configuration</code> 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 <code>configFilename</code> if it |
| exists. Moreover, a thread will be created that will periodically |
| check if <code>configFilename</code> has been created or |
| modified. The period is determined by the <code>delay</code> |
| argument. If a change or file creation is detected, then |
| <code>configFilename</code> 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 { |
| return parser.parse(url.toString()); |
| } |
| 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) { |
| // 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 <a |
| href="doc-files/log4j.dtd">log4j.dtd</a>. |
| |
| */ |
| 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."); |
| } |
| |
| |
| 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(CATEGORY_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 OptionHandler 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 OptionHandler 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 instanceof OptionHandler) { |
| OptionHandler optionHandler = (OptionHandler) instance; |
| PropertySetter propSetter = new PropertySetter(optionHandler); |
| 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 optionHandler; |
| } |
| return null; |
| } |
| |
| private static class XMLWatchdog extends FileWatchdog { |
| |
| XMLWatchdog(String filename) { |
| super(filename); |
| } |
| |
| /** |
| Call {@link PropertyConfigurator#configure(String)} with the |
| <code>filename</code> to reconfigure log4j. */ |
| public |
| void doOnChange() { |
| new PluginConfigurator().doConfigure(filename, |
| LogManager.getLoggerRepository()); |
| } |
| } |
| |
| |
| /** |
| * An {@link EntityResolver} specifically designed to return |
| * <code>log4j.dtd</code> which is embedded within the log4j jar |
| * file. |
| * |
| * @author Paul Austin |
| * */ |
| private static class Log4jEntityResolver implements EntityResolver { |
| |
| public InputSource resolveEntity (String publicId, String systemId) { |
| if (systemId.endsWith("log4j.dtd")) { |
| Class clazz = getClass(); |
| InputStream in = Log4jEntityResolver.class.getResourceAsStream("log4j.dtd"); |
| if (in == null) { |
| LogLog.error("Could not find [log4j.dtd]. Used [" + clazz.getClassLoader() |
| + "] class loader in the search."); |
| return null; |
| } else { |
| return new InputSource(in); |
| } |
| } else { |
| return null; |
| } |
| } |
| } |
| |
| |
| } |
| |
| |