2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 package org.apache.log4j.xml;
20 import org.apache.log4j.Appender;
21 import org.apache.log4j.Layout;
22 import org.apache.log4j.Level;
23 import org.apache.log4j.LogManager;
24 import org.apache.log4j.Logger;
25 import org.apache.log4j.config.PropertySetter;
26 import org.apache.log4j.helpers.FileWatchdog;
27 import org.apache.log4j.helpers.Loader;
28 import org.apache.log4j.helpers.LogLog;
29 import org.apache.log4j.helpers.OptionConverter;
30 import org.apache.log4j.or.RendererMap;
31 import org.apache.log4j.spi.AppenderAttachable;
32 import org.apache.log4j.spi.Configurator;
33 import org.apache.log4j.spi.ErrorHandler;
34 import org.apache.log4j.spi.Filter;
35 import org.apache.log4j.spi.LoggerFactory;
36 import org.apache.log4j.spi.LoggerRepository;
37 import org.apache.log4j.spi.RendererSupport;
38 import org.apache.log4j.spi.ThrowableRenderer;
39 import org.apache.log4j.spi.ThrowableRendererSupport;
40 import org.w3c.dom.Document;
41 import org.w3c.dom.Element;
42 import org.w3c.dom.NamedNodeMap;
43 import org.w3c.dom.Node;
44 import org.w3c.dom.NodeList;
45 import org.xml.sax.InputSource;
46 import org.xml.sax.SAXException;
48 import javax.xml.parsers.DocumentBuilder;
49 import javax.xml.parsers.DocumentBuilderFactory;
50 import javax.xml.parsers.FactoryConfigurationError;
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.io.InterruptedIOException;
55 import java.io.Reader;
56 import java.lang.reflect.Method;
57 import java.lang.reflect.InvocationTargetException;
59 import java.net.URLConnection;
60 import java.util.Hashtable;
61 import java.util.Properties;
63 // Contributors: Mark Womack
67 Use this class to initialize the log4j environment using a DOM tree.
69 <p>The DTD is specified in <a
70 href="doc-files/log4j.dtd"><b>log4j.dtd</b></a>.
72 <p>Sometimes it is useful to see how log4j is reading configuration
73 files. You can enable log4j internal logging by defining the
74 <b>log4j.debug</b> variable on the java command
75 line. Alternatively, set the <code>debug</code> attribute in the
76 <code>log4j:configuration</code> element. As in
78 <log4j:configuration <b>debug="true"</b> xmlns:log4j="http://jakarta.apache.org/log4j/">
80 </log4j:configuration>
83 <p>There are sample XML files included in the package.
85 @author Christopher Taylor
86 @author Ceki Gülcü
87 @author Anders Kristensen
90 public class DOMConfigurator implements Configurator {
92 static final String CONFIGURATION_TAG = "log4j:configuration";
93 static final String OLD_CONFIGURATION_TAG = "configuration";
94 static final String RENDERER_TAG = "renderer";
95 private static final String THROWABLE_RENDERER_TAG = "throwableRenderer";
96 static final String APPENDER_TAG = "appender";
97 static final String APPENDER_REF_TAG = "appender-ref";
98 static final String PARAM_TAG = "param";
99 static final String LAYOUT_TAG = "layout";
100 static final String CATEGORY = "category";
101 static final String LOGGER = "logger";
102 static final String LOGGER_REF = "logger-ref";
103 static final String CATEGORY_FACTORY_TAG = "categoryFactory";
104 static final String LOGGER_FACTORY_TAG = "loggerFactory";
105 static final String NAME_ATTR = "name";
106 static final String CLASS_ATTR = "class";
107 static final String VALUE_ATTR = "value";
108 static final String ROOT_TAG = "root";
109 static final String ROOT_REF = "root-ref";
110 static final String LEVEL_TAG = "level";
111 static final String PRIORITY_TAG = "priority";
112 static final String FILTER_TAG = "filter";
113 static final String ERROR_HANDLER_TAG = "errorHandler";
114 static final String REF_ATTR = "ref";
115 static final String ADDITIVITY_ATTR = "additivity";
116 static final String THRESHOLD_ATTR = "threshold";
117 static final String CONFIG_DEBUG_ATTR = "configDebug";
118 static final String INTERNAL_DEBUG_ATTR = "debug";
119 private static final String RESET_ATTR = "reset";
120 static final String RENDERING_CLASS_ATTR = "renderingClass";
121 static final String RENDERED_CLASS_ATTR = "renderedClass";
123 static final String EMPTY_STR = "";
124 static final Class[] ONE_STRING_PARAM = new Class[] {String.class};
126 final static String dbfKey = "javax.xml.parsers.DocumentBuilderFactory";
129 // key: appenderName, value: appender
130 Hashtable appenderBag;
133 LoggerRepository repository;
135 protected LoggerFactory catFactory = null;
138 No argument constructor.
142 appenderBag = new Hashtable();
146 Used internally to parse appenders by IDREF name.
149 Appender findAppenderByName(Document doc, String appenderName) {
150 Appender appender = (Appender) appenderBag.get(appenderName);
152 if(appender != null) {
155 // Doesn't work on DOM Level 1 :
156 // Element element = doc.getElementById(appenderName);
159 Element element = null;
160 NodeList list = doc.getElementsByTagName("appender");
161 for (int t=0; t < list.getLength(); t++) {
162 Node node = list.item(t);
163 NamedNodeMap map= node.getAttributes();
164 Node attrNode = map.getNamedItem("name");
165 if (appenderName.equals(attrNode.getNodeValue())) {
166 element = (Element) node;
172 if(element == null) {
173 LogLog.error("No appender named ["+appenderName+"] could be found.");
176 appender = parseAppender(element);
177 if (appender != null) {
178 appenderBag.put(appenderName, appender);
185 Used internally to parse appenders by IDREF element.
188 Appender findAppenderByReference(Element appenderRef) {
189 String appenderName = subst(appenderRef.getAttribute(REF_ATTR));
190 Document doc = appenderRef.getOwnerDocument();
191 return findAppenderByName(doc, appenderName);
195 * Delegates unrecognized content to created instance if
196 * it supports UnrecognizedElementParser.
198 * @param instance instance, may be null.
199 * @param element element, may not be null.
200 * @param props properties
201 * @throws IOException thrown if configuration of owner object
202 * should be abandoned.
204 private static void parseUnrecognizedElement(final Object instance,
205 final Element element,
206 final Properties props) throws Exception {
207 boolean recognized = false;
208 if (instance instanceof UnrecognizedElementHandler) {
209 recognized = ((UnrecognizedElementHandler) instance).parseUnrecognizedElement(
213 LogLog.warn("Unrecognized element " + element.getNodeName());
218 * Delegates unrecognized content to created instance if
219 * it supports UnrecognizedElementParser and catches and
220 * logs any exception.
222 * @param instance instance, may be null.
223 * @param element element, may not be null.
224 * @param props properties
226 private static void quietParseUnrecognizedElement(final Object instance,
227 final Element element,
228 final Properties props) {
230 parseUnrecognizedElement(instance, element, props);
231 } catch (Exception ex) {
232 if (ex instanceof InterruptedException || ex instanceof InterruptedIOException) {
233 Thread.currentThread().interrupt();
235 LogLog.error("Error in extension content: ", ex);
240 Used internally to parse an appender element.
243 Appender parseAppender (Element appenderElement) {
244 String className = subst(appenderElement.getAttribute(CLASS_ATTR));
245 LogLog.debug("Class name: [" + className+']');
247 Object instance = Loader.loadClass(className).newInstance();
248 Appender appender = (Appender)instance;
249 PropertySetter propSetter = new PropertySetter(appender);
251 appender.setName(subst(appenderElement.getAttribute(NAME_ATTR)));
253 NodeList children = appenderElement.getChildNodes();
254 final int length = children.getLength();
256 for (int loop = 0; loop < length; loop++) {
257 Node currentNode = children.item(loop);
259 /* We're only interested in Elements */
260 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
261 Element currentElement = (Element)currentNode;
263 // Parse appender parameters
264 if (currentElement.getTagName().equals(PARAM_TAG)) {
265 setParameter(currentElement, propSetter);
267 // Set appender layout
268 else if (currentElement.getTagName().equals(LAYOUT_TAG)) {
269 appender.setLayout(parseLayout(currentElement));
272 else if (currentElement.getTagName().equals(FILTER_TAG)) {
273 parseFilters(currentElement, appender);
275 else if (currentElement.getTagName().equals(ERROR_HANDLER_TAG)) {
276 parseErrorHandler(currentElement, appender);
278 else if (currentElement.getTagName().equals(APPENDER_REF_TAG)) {
279 String refName = subst(currentElement.getAttribute(REF_ATTR));
280 if(appender instanceof AppenderAttachable) {
281 AppenderAttachable aa = (AppenderAttachable) appender;
282 LogLog.debug("Attaching appender named ["+ refName+
283 "] to appender named ["+ appender.getName()+"].");
284 aa.addAppender(findAppenderByReference(currentElement));
286 LogLog.error("Requesting attachment of appender named ["+
287 refName+ "] to appender named ["+ appender.getName()+
288 "] which does not implement org.apache.log4j.spi.AppenderAttachable.");
291 parseUnrecognizedElement(instance, currentElement, props);
295 propSetter.activate();
298 /* Yes, it's ugly. But all of these exceptions point to the same
299 problem: we can't create an Appender */
300 catch (Exception oops) {
301 if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
302 Thread.currentThread().interrupt();
304 LogLog.error("Could not create an Appender. Reported error follows.",
311 Used internally to parse an {@link ErrorHandler} element.
314 void parseErrorHandler(Element element, Appender appender) {
315 ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName(
316 subst(element.getAttribute(CLASS_ATTR)),
317 org.apache.log4j.spi.ErrorHandler.class,
321 eh.setAppender(appender);
323 PropertySetter propSetter = new PropertySetter(eh);
324 NodeList children = element.getChildNodes();
325 final int length = children.getLength();
327 for (int loop = 0; loop < length; loop++) {
328 Node currentNode = children.item(loop);
329 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
330 Element currentElement = (Element) currentNode;
331 String tagName = currentElement.getTagName();
332 if(tagName.equals(PARAM_TAG)) {
333 setParameter(currentElement, propSetter);
334 } else if(tagName.equals(APPENDER_REF_TAG)) {
335 eh.setBackupAppender(findAppenderByReference(currentElement));
336 } else if(tagName.equals(LOGGER_REF)) {
337 String loggerName = currentElement.getAttribute(REF_ATTR);
338 Logger logger = (catFactory == null) ? repository.getLogger(loggerName)
339 : repository.getLogger(loggerName, catFactory);
340 eh.setLogger(logger);
341 } else if(tagName.equals(ROOT_REF)) {
342 Logger root = repository.getRootLogger();
345 quietParseUnrecognizedElement(eh, currentElement, props);
349 propSetter.activate();
350 appender.setErrorHandler(eh);
355 Used internally to parse a filter element.
358 void parseFilters(Element element, Appender appender) {
359 String clazz = subst(element.getAttribute(CLASS_ATTR));
360 Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz,
364 PropertySetter propSetter = new PropertySetter(filter);
365 NodeList children = element.getChildNodes();
366 final int length = children.getLength();
368 for (int loop = 0; loop < length; loop++) {
369 Node currentNode = children.item(loop);
370 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
371 Element currentElement = (Element) currentNode;
372 String tagName = currentElement.getTagName();
373 if(tagName.equals(PARAM_TAG)) {
374 setParameter(currentElement, propSetter);
376 quietParseUnrecognizedElement(filter, currentElement, props);
380 propSetter.activate();
381 LogLog.debug("Adding filter of type ["+filter.getClass()
382 +"] to appender named ["+appender.getName()+"].");
383 appender.addFilter(filter);
388 Used internally to parse an category element.
391 void parseCategory (Element loggerElement) {
392 // Create a new org.apache.log4j.Category object from the <category> element.
393 String catName = subst(loggerElement.getAttribute(NAME_ATTR));
397 String className = subst(loggerElement.getAttribute(CLASS_ATTR));
400 if(EMPTY_STR.equals(className)) {
401 LogLog.debug("Retreiving an instance of org.apache.log4j.Logger.");
402 cat = (catFactory == null) ? repository.getLogger(catName) : repository.getLogger(catName, catFactory);
405 LogLog.debug("Desired logger sub-class: ["+className+']');
407 Class clazz = Loader.loadClass(className);
408 Method getInstanceMethod = clazz.getMethod("getLogger",
410 cat = (Logger) getInstanceMethod.invoke(null, new Object[] {catName});
411 } catch (InvocationTargetException oops) {
412 if (oops.getTargetException() instanceof InterruptedException
413 || oops.getTargetException() instanceof InterruptedIOException) {
414 Thread.currentThread().interrupt();
416 LogLog.error("Could not retrieve category ["+catName+
417 "]. Reported error follows.", oops);
419 } catch (Exception oops) {
420 LogLog.error("Could not retrieve category ["+catName+
421 "]. Reported error follows.", oops);
426 // Setting up a category needs to be an atomic operation, in order
427 // to protect potential log operations while category
428 // configuration is in progress.
430 boolean additivity = OptionConverter.toBoolean(
431 subst(loggerElement.getAttribute(ADDITIVITY_ATTR)),
434 LogLog.debug("Setting ["+cat.getName()+"] additivity to ["+additivity+"].");
435 cat.setAdditivity(additivity);
436 parseChildrenOfLoggerElement(loggerElement, cat, false);
442 Used internally to parse the category factory element.
445 void parseCategoryFactory(Element factoryElement) {
446 String className = subst(factoryElement.getAttribute(CLASS_ATTR));
448 if(EMPTY_STR.equals(className)) {
449 LogLog.error("Category Factory tag " + CLASS_ATTR + " attribute not found.");
450 LogLog.debug("No Category Factory configured.");
453 LogLog.debug("Desired category factory: ["+className+']');
454 Object factory = OptionConverter.instantiateByClassName(className,
457 if (factory instanceof LoggerFactory) {
458 catFactory = (LoggerFactory) factory;
460 LogLog.error("Category Factory class " + className + " does not implement org.apache.log4j.LoggerFactory");
462 PropertySetter propSetter = new PropertySetter(factory);
464 Element currentElement = null;
465 Node currentNode = null;
466 NodeList children = factoryElement.getChildNodes();
467 final int length = children.getLength();
469 for (int loop=0; loop < length; loop++) {
470 currentNode = children.item(loop);
471 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
472 currentElement = (Element)currentNode;
473 if (currentElement.getTagName().equals(PARAM_TAG)) {
474 setParameter(currentElement, propSetter);
476 quietParseUnrecognizedElement(factory, currentElement, props);
485 Used internally to parse the roor category element.
488 void parseRoot (Element rootElement) {
489 Logger root = repository.getRootLogger();
490 // category configuration needs to be atomic
492 parseChildrenOfLoggerElement(rootElement, root, true);
498 Used internally to parse the children of a category element.
501 void parseChildrenOfLoggerElement(Element catElement,
502 Logger cat, boolean isRoot) {
504 PropertySetter propSetter = new PropertySetter(cat);
506 // Remove all existing appenders from cat. They will be
507 // reconstructed if need be.
508 cat.removeAllAppenders();
511 NodeList children = catElement.getChildNodes();
512 final int length = children.getLength();
514 for (int loop = 0; loop < length; loop++) {
515 Node currentNode = children.item(loop);
517 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
518 Element currentElement = (Element) currentNode;
519 String tagName = currentElement.getTagName();
521 if (tagName.equals(APPENDER_REF_TAG)) {
522 Element appenderRef = (Element) currentNode;
523 Appender appender = findAppenderByReference(appenderRef);
524 String refName = subst(appenderRef.getAttribute(REF_ATTR));
525 if(appender != null) {
526 LogLog.debug("Adding appender named ["+ refName+
527 "] to category ["+cat.getName()+"].");
529 LogLog.debug("Appender named ["+ refName + "] not found.");
532 cat.addAppender(appender);
534 } else if(tagName.equals(LEVEL_TAG)) {
535 parseLevel(currentElement, cat, isRoot);
536 } else if(tagName.equals(PRIORITY_TAG)) {
537 parseLevel(currentElement, cat, isRoot);
538 } else if(tagName.equals(PARAM_TAG)) {
539 setParameter(currentElement, propSetter);
541 quietParseUnrecognizedElement(cat, currentElement, props);
545 propSetter.activate();
549 Used internally to parse a layout element.
552 Layout parseLayout (Element layout_element) {
553 String className = subst(layout_element.getAttribute(CLASS_ATTR));
554 LogLog.debug("Parsing layout of class: \""+className+"\"");
556 Object instance = Loader.loadClass(className).newInstance();
557 Layout layout = (Layout)instance;
558 PropertySetter propSetter = new PropertySetter(layout);
560 NodeList params = layout_element.getChildNodes();
561 final int length = params.getLength();
563 for (int loop = 0; loop < length; loop++) {
564 Node currentNode = params.item(loop);
565 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
566 Element currentElement = (Element) currentNode;
567 String tagName = currentElement.getTagName();
568 if(tagName.equals(PARAM_TAG)) {
569 setParameter(currentElement, propSetter);
571 parseUnrecognizedElement(instance, currentElement, props);
576 propSetter.activate();
579 catch (Exception oops) {
580 if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
581 Thread.currentThread().interrupt();
583 LogLog.error("Could not create the Layout. Reported error follows.",
590 void parseRenderer(Element element) {
591 String renderingClass = subst(element.getAttribute(RENDERING_CLASS_ATTR));
592 String renderedClass = subst(element.getAttribute(RENDERED_CLASS_ATTR));
593 if(repository instanceof RendererSupport) {
594 RendererMap.addRenderer((RendererSupport) repository, renderedClass,
600 * Parses throwable renderer.
601 * @param element throwableRenderer element.
602 * @return configured throwable renderer.
605 protected ThrowableRenderer parseThrowableRenderer(final Element element) {
606 String className = subst(element.getAttribute(CLASS_ATTR));
607 LogLog.debug("Parsing throwableRenderer of class: \""+className+"\"");
609 Object instance = Loader.loadClass(className).newInstance();
610 ThrowableRenderer tr = (ThrowableRenderer)instance;
611 PropertySetter propSetter = new PropertySetter(tr);
613 NodeList params = element.getChildNodes();
614 final int length = params.getLength();
616 for (int loop = 0; loop < length; loop++) {
617 Node currentNode = params.item(loop);
618 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
619 Element currentElement = (Element) currentNode;
620 String tagName = currentElement.getTagName();
621 if(tagName.equals(PARAM_TAG)) {
622 setParameter(currentElement, propSetter);
624 parseUnrecognizedElement(instance, currentElement, props);
629 propSetter.activate();
632 catch (Exception oops) {
633 if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
634 Thread.currentThread().interrupt();
636 LogLog.error("Could not create the ThrowableRenderer. Reported error follows.",
643 Used internally to parse a level element.
646 void parseLevel(Element element, Logger logger, boolean isRoot) {
647 String catName = logger.getName();
652 String priStr = subst(element.getAttribute(VALUE_ATTR));
653 LogLog.debug("Level value for "+catName+" is ["+priStr+"].");
655 if(INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) {
657 LogLog.error("Root level cannot be inherited. Ignoring directive.");
659 logger.setLevel(null);
662 String className = subst(element.getAttribute(CLASS_ATTR));
663 if(EMPTY_STR.equals(className)) {
664 logger.setLevel(OptionConverter.toLevel(priStr, Level.DEBUG));
666 LogLog.debug("Desired Level sub-class: ["+className+']');
668 Class clazz = Loader.loadClass(className);
669 Method toLevelMethod = clazz.getMethod("toLevel",
671 Level pri = (Level) toLevelMethod.invoke(null,
672 new Object[] {priStr});
673 logger.setLevel(pri);
674 } catch (Exception oops) {
675 if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
676 Thread.currentThread().interrupt();
678 LogLog.error("Could not create level ["+priStr+
679 "]. Reported error follows.", oops);
684 LogLog.debug(catName + " level set to " + logger.getLevel());
688 void setParameter(Element elem, PropertySetter propSetter) {
689 String name = subst(elem.getAttribute(NAME_ATTR));
690 String value = (elem.getAttribute(VALUE_ATTR));
691 value = subst(OptionConverter.convertSpecialChars(value));
692 propSetter.setProperty(name, value);
697 Configure log4j using a <code>configuration</code> element as
698 defined in the log4j.dtd.
703 void configure (Element element) {
704 DOMConfigurator configurator = new DOMConfigurator();
705 configurator.doConfigure(element, LogManager.getLoggerRepository());
709 Like {@link #configureAndWatch(String, long)} except that the
710 default delay as defined by {@link FileWatchdog#DEFAULT_DELAY} is
713 @param configFilename A log4j configuration file in XML format.
718 void configureAndWatch(String configFilename) {
719 configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY);
723 Read the configuration file <code>configFilename</code> if it
724 exists. Moreover, a thread will be created that will periodically
725 check if <code>configFilename</code> has been created or
726 modified. The period is determined by the <code>delay</code>
727 argument. If a change or file creation is detected, then
728 <code>configFilename</code> is read to configure log4j.
730 @param configFilename A log4j configuration file in XML format.
731 @param delay The delay in milliseconds to wait between each check.
735 void configureAndWatch(String configFilename, long delay) {
736 XMLWatchdog xdog = new XMLWatchdog(configFilename);
737 xdog.setDelay(delay);
741 private interface ParseAction {
742 Document parse(final DocumentBuilder parser) throws SAXException, IOException;
747 void doConfigure(final String filename, LoggerRepository repository) {
748 ParseAction action = new ParseAction() {
749 public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
750 return parser.parse(new File(filename));
752 public String toString() {
753 return "file [" + filename + "]";
756 doConfigure(action, repository);
761 void doConfigure(final URL url, LoggerRepository repository) {
762 ParseAction action = new ParseAction() {
763 public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
764 URLConnection uConn = url.openConnection();
765 uConn.setUseCaches(false);
766 InputStream stream = uConn.getInputStream();
768 InputSource src = new InputSource(stream);
769 src.setSystemId(url.toString());
770 return parser.parse(src);
775 public String toString() {
776 return "url [" + url.toString() + "]";
779 doConfigure(action, repository);
783 Configure log4j by reading in a log4j.dtd compliant XML
788 void doConfigure(final InputStream inputStream, LoggerRepository repository)
789 throws FactoryConfigurationError {
790 ParseAction action = new ParseAction() {
791 public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
792 InputSource inputSource = new InputSource(inputStream);
793 inputSource.setSystemId("dummy://log4j.dtd");
794 return parser.parse(inputSource);
796 public String toString() {
797 return "input stream [" + inputStream.toString() + "]";
800 doConfigure(action, repository);
804 Configure log4j by reading in a log4j.dtd compliant XML
809 void doConfigure(final Reader reader, LoggerRepository repository)
810 throws FactoryConfigurationError {
811 ParseAction action = new ParseAction() {
812 public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
813 InputSource inputSource = new InputSource(reader);
814 inputSource.setSystemId("dummy://log4j.dtd");
815 return parser.parse(inputSource);
817 public String toString() {
818 return "reader [" + reader.toString() + "]";
821 doConfigure(action, repository);
825 Configure log4j by reading in a log4j.dtd compliant XML
830 void doConfigure(final InputSource inputSource, LoggerRepository repository)
831 throws FactoryConfigurationError {
832 if (inputSource.getSystemId() == null) {
833 inputSource.setSystemId("dummy://log4j.dtd");
835 ParseAction action = new ParseAction() {
836 public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
837 return parser.parse(inputSource);
839 public String toString() {
840 return "input source [" + inputSource.toString() + "]";
843 doConfigure(action, repository);
847 private final void doConfigure(final ParseAction action, final LoggerRepository repository)
848 throws FactoryConfigurationError {
849 DocumentBuilderFactory dbf = null;
850 this.repository = repository;
852 LogLog.debug("System property is :"+
853 OptionConverter.getSystemProperty(dbfKey,
855 dbf = DocumentBuilderFactory.newInstance();
856 LogLog.debug("Standard DocumentBuilderFactory search succeded.");
857 LogLog.debug("DocumentBuilderFactory is: "+dbf.getClass().getName());
858 } catch(FactoryConfigurationError fce) {
859 Exception e = fce.getException();
860 LogLog.debug("Could not instantiate a DocumentBuilderFactory.", e);
865 dbf.setValidating(true);
867 DocumentBuilder docBuilder = dbf.newDocumentBuilder();
869 docBuilder.setErrorHandler(new SAXErrorHandler());
870 docBuilder.setEntityResolver(new Log4jEntityResolver());
872 Document doc = action.parse(docBuilder);
873 parse(doc.getDocumentElement());
874 } catch (Exception e) {
875 if (e instanceof InterruptedException || e instanceof InterruptedIOException) {
876 Thread.currentThread().interrupt();
878 // I know this is miserable...
879 LogLog.error("Could not parse "+ action.toString() + ".", e);
884 Configure by taking in an DOM element.
886 public void doConfigure(Element element, LoggerRepository repository) {
887 this.repository = repository;
893 A static version of {@link #doConfigure(String, LoggerRepository)}. */
896 void configure(String filename) throws FactoryConfigurationError {
897 new DOMConfigurator().doConfigure(filename,
898 LogManager.getLoggerRepository());
902 A static version of {@link #doConfigure(URL, LoggerRepository)}.
906 void configure(URL url) throws FactoryConfigurationError {
907 new DOMConfigurator().doConfigure(url, LogManager.getLoggerRepository());
911 Used internally to configure the log4j framework by parsing a DOM
912 tree of XML elements based on <a
913 href="doc-files/log4j.dtd">log4j.dtd</a>.
917 void parse(Element element) {
919 String rootElementName = element.getTagName();
921 if (!rootElementName.equals(CONFIGURATION_TAG)) {
922 if(rootElementName.equals(OLD_CONFIGURATION_TAG)) {
923 LogLog.warn("The <"+OLD_CONFIGURATION_TAG+
924 "> element has been deprecated.");
925 LogLog.warn("Use the <"+CONFIGURATION_TAG+"> element instead.");
927 LogLog.error("DOM element is - not a <"+CONFIGURATION_TAG+"> element.");
933 String debugAttrib = subst(element.getAttribute(INTERNAL_DEBUG_ATTR));
935 LogLog.debug("debug attribute= \"" + debugAttrib +"\".");
936 // if the log4j.dtd is not specified in the XML file, then the
937 // "debug" attribute is returned as the empty string.
938 if(!debugAttrib.equals("") && !debugAttrib.equals("null")) {
939 LogLog.setInternalDebugging(OptionConverter.toBoolean(debugAttrib, true));
941 LogLog.debug("Ignoring " + INTERNAL_DEBUG_ATTR + " attribute.");
945 // reset repository before configuration if reset="true"
946 // on configuration element.
948 String resetAttrib = subst(element.getAttribute(RESET_ATTR));
949 LogLog.debug("reset attribute= \"" + resetAttrib +"\".");
950 if(!("".equals(resetAttrib))) {
951 if (OptionConverter.toBoolean(resetAttrib, false)) {
952 repository.resetConfiguration();
958 String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR));
959 if(!confDebug.equals("") && !confDebug.equals("null")) {
960 LogLog.warn("The \""+CONFIG_DEBUG_ATTR+"\" attribute is deprecated.");
961 LogLog.warn("Use the \""+INTERNAL_DEBUG_ATTR+"\" attribute instead.");
962 LogLog.setInternalDebugging(OptionConverter.toBoolean(confDebug, true));
965 String thresholdStr = subst(element.getAttribute(THRESHOLD_ATTR));
966 LogLog.debug("Threshold =\"" + thresholdStr +"\".");
967 if(!"".equals(thresholdStr) && !"null".equals(thresholdStr)) {
968 repository.setThreshold(thresholdStr);
971 //Hashtable appenderBag = new Hashtable(11);
973 /* Building Appender objects, placing them in a local namespace
974 for future reference */
976 // First configure each category factory under the root element.
977 // Category factories need to be configured before any of
978 // categories they support.
980 String tagName = null;
981 Element currentElement = null;
982 Node currentNode = null;
983 NodeList children = element.getChildNodes();
984 final int length = children.getLength();
986 for (int loop = 0; loop < length; loop++) {
987 currentNode = children.item(loop);
988 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
989 currentElement = (Element) currentNode;
990 tagName = currentElement.getTagName();
992 if (tagName.equals(CATEGORY_FACTORY_TAG) || tagName.equals(LOGGER_FACTORY_TAG)) {
993 parseCategoryFactory(currentElement);
998 for (int loop = 0; loop < length; loop++) {
999 currentNode = children.item(loop);
1000 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
1001 currentElement = (Element) currentNode;
1002 tagName = currentElement.getTagName();
1004 if (tagName.equals(CATEGORY) || tagName.equals(LOGGER)) {
1005 parseCategory(currentElement);
1006 } else if (tagName.equals(ROOT_TAG)) {
1007 parseRoot(currentElement);
1008 } else if(tagName.equals(RENDERER_TAG)) {
1009 parseRenderer(currentElement);
1010 } else if(tagName.equals(THROWABLE_RENDERER_TAG)) {
1011 if (repository instanceof ThrowableRendererSupport) {
1012 ThrowableRenderer tr = parseThrowableRenderer(currentElement);
1014 ((ThrowableRendererSupport) repository).setThrowableRenderer(tr);
1017 } else if (!(tagName.equals(APPENDER_TAG)
1018 || tagName.equals(CATEGORY_FACTORY_TAG)
1019 || tagName.equals(LOGGER_FACTORY_TAG))) {
1020 quietParseUnrecognizedElement(repository, currentElement, props);
1028 String subst(final String value) {
1029 return subst(value, props);
1033 * Substitutes property value for any references in expression.
1035 * @param value value from configuration file, may contain
1036 * literal text, property references or both
1037 * @param props properties.
1038 * @return evaluated expression, may still contain expressions
1039 * if unable to expand.
1042 public static String subst(final String value, final Properties props) {
1044 return OptionConverter.substVars(value, props);
1045 } catch (IllegalArgumentException e) {
1046 LogLog.warn("Could not perform variable substitution.", e);
1053 * Sets a parameter based from configuration file content.
1055 * @param elem param element, may not be null.
1056 * @param propSetter property setter, may not be null.
1057 * @param props properties
1060 public static void setParameter(final Element elem,
1061 final PropertySetter propSetter,
1062 final Properties props) {
1063 String name = subst(elem.getAttribute("name"), props);
1064 String value = (elem.getAttribute("value"));
1065 value = subst(OptionConverter.convertSpecialChars(value), props);
1066 propSetter.setProperty(name, value);
1070 * Creates an object and processes any nested param elements
1071 * but does not call activateOptions. If the class also supports
1072 * UnrecognizedElementParser, the parseUnrecognizedElement method
1073 * will be call for any child elements other than param.
1075 * @param element element, may not be null.
1076 * @param props properties
1077 * @param expectedClass interface or class expected to be implemented
1079 * @return created class or null.
1080 * @throws Exception thrown if the contain object should be abandoned.
1083 public static Object parseElement(final Element element,
1084 final Properties props,
1085 final Class expectedClass) throws Exception {
1086 String clazz = subst(element.getAttribute("class"), props);
1087 Object instance = OptionConverter.instantiateByClassName(clazz,
1088 expectedClass, null);
1090 if (instance != null) {
1091 PropertySetter propSetter = new PropertySetter(instance);
1092 NodeList children = element.getChildNodes();
1093 final int length = children.getLength();
1095 for (int loop = 0; loop < length; loop++) {
1096 Node currentNode = children.item(loop);
1097 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
1098 Element currentElement = (Element) currentNode;
1099 String tagName = currentElement.getTagName();
1100 if (tagName.equals("param")) {
1101 setParameter(currentElement, propSetter, props);
1103 parseUnrecognizedElement(instance, currentElement, props);
1115 class XMLWatchdog extends FileWatchdog {
1117 XMLWatchdog(String filename) {
1122 Call {@link DOMConfigurator#configure(String)} with the
1123 <code>filename</code> to reconfigure log4j. */
1126 new DOMConfigurator().doConfigure(filename,
1127 LogManager.getLoggerRepository());