001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.log4j.xml; 018 019import java.io.IOException; 020import java.io.InterruptedIOException; 021import java.lang.reflect.Method; 022import java.util.HashMap; 023import java.util.Map; 024import java.util.Properties; 025import java.util.function.Consumer; 026 027import javax.xml.parsers.DocumentBuilder; 028import javax.xml.parsers.DocumentBuilderFactory; 029import javax.xml.parsers.FactoryConfigurationError; 030 031import org.apache.log4j.Appender; 032import org.apache.log4j.Layout; 033import org.apache.log4j.Level; 034import org.apache.log4j.bridge.AppenderAdapter; 035import org.apache.log4j.bridge.AppenderWrapper; 036import org.apache.log4j.config.Log4j1Configuration; 037import org.apache.log4j.config.PropertySetter; 038import org.apache.log4j.helpers.OptionConverter; 039import org.apache.log4j.rewrite.RewritePolicy; 040import org.apache.log4j.spi.AppenderAttachable; 041import org.apache.log4j.spi.ErrorHandler; 042import org.apache.log4j.spi.Filter; 043import org.apache.logging.log4j.core.LoggerContext; 044import org.apache.logging.log4j.core.config.Configuration; 045import org.apache.logging.log4j.core.config.ConfigurationSource; 046import org.apache.logging.log4j.core.config.LoggerConfig; 047import org.apache.logging.log4j.core.config.status.StatusConfiguration; 048import org.apache.logging.log4j.status.StatusLogger; 049import org.apache.logging.log4j.util.LoaderUtil; 050import org.w3c.dom.Document; 051import org.w3c.dom.Element; 052import org.w3c.dom.NamedNodeMap; 053import org.w3c.dom.Node; 054import org.w3c.dom.NodeList; 055import org.xml.sax.InputSource; 056import org.xml.sax.SAXException; 057import org.xml.sax.SAXParseException; 058 059/** 060 * Class Description goes here. 061 */ 062public class XmlConfiguration extends Log4j1Configuration { 063 064 private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger(); 065 066 private static final String CONFIGURATION_TAG = "log4j:configuration"; 067 private static final String OLD_CONFIGURATION_TAG = "configuration"; 068 private static final String RENDERER_TAG = "renderer"; 069 private static final String APPENDER_TAG = "appender"; 070 public static final String PARAM_TAG = "param"; 071 public static final String LAYOUT_TAG = "layout"; 072 private static final String CATEGORY = "category"; 073 private static final String LOGGER_ELEMENT = "logger"; 074 private static final String CATEGORY_FACTORY_TAG = "categoryFactory"; 075 private static final String LOGGER_FACTORY_TAG = "loggerFactory"; 076 public static final String NAME_ATTR = "name"; 077 private static final String CLASS_ATTR = "class"; 078 public static final String VALUE_ATTR = "value"; 079 private static final String ROOT_TAG = "root"; 080 private static final String LEVEL_TAG = "level"; 081 private static final String PRIORITY_TAG = "priority"; 082 public static final String FILTER_TAG = "filter"; 083 private static final String ERROR_HANDLER_TAG = "errorHandler"; 084 public static final String REF_ATTR = "ref"; 085 private static final String ADDITIVITY_ATTR = "additivity"; 086 private static final String CONFIG_DEBUG_ATTR = "configDebug"; 087 private static final String INTERNAL_DEBUG_ATTR = "debug"; 088 private static final String EMPTY_STR = ""; 089 private static final Class<?>[] ONE_STRING_PARAM = new Class[] { String.class }; 090 private static final String dbfKey = "javax.xml.parsers.DocumentBuilderFactory"; 091 private static final String THROWABLE_RENDERER_TAG = "throwableRenderer"; 092 093 public static final long DEFAULT_DELAY = 60000; 094 095 /** 096 * File name prefix for test configurations. 097 */ 098 protected static final String TEST_PREFIX = "log4j-test"; 099 100 /** 101 * File name prefix for standard configurations. 102 */ 103 protected static final String DEFAULT_PREFIX = "log4j"; 104 105 // key: appenderName, value: appender 106 private Map<String, Appender> appenderMap; 107 108 private Properties props = null; 109 110 public XmlConfiguration(final LoggerContext loggerContext, final ConfigurationSource source, 111 int monitorIntervalSeconds) { 112 super(loggerContext, source, monitorIntervalSeconds); 113 appenderMap = new HashMap<>(); 114 } 115 116 public void addAppenderIfAbsent(Appender appender) { 117 appenderMap.putIfAbsent(appender.getName(), appender); 118 } 119 120 /** 121 * Configures log4j by reading in a log4j.dtd compliant XML 122 * configuration file. 123 */ 124 @Override 125 public void doConfigure() throws FactoryConfigurationError { 126 ConfigurationSource source = getConfigurationSource(); 127 ParseAction action = new ParseAction() { 128 @Override 129 public Document parse(final DocumentBuilder parser) throws SAXException, IOException { 130 @SuppressWarnings("resource") // The ConfigurationSource and its caller manages the InputStream. 131 InputSource inputSource = new InputSource(source.getInputStream()); 132 inputSource.setSystemId("dummy://log4j.dtd"); 133 return parser.parse(inputSource); 134 } 135 136 @Override 137 public String toString() { 138 return getConfigurationSource().getLocation(); 139 } 140 }; 141 doConfigure(action); 142 } 143 144 private void doConfigure(final ParseAction action) throws FactoryConfigurationError { 145 DocumentBuilderFactory dbf; 146 try { 147 LOGGER.debug("System property is : {}", OptionConverter.getSystemProperty(dbfKey, null)); 148 dbf = DocumentBuilderFactory.newInstance(); 149 LOGGER.debug("Standard DocumentBuilderFactory search succeded."); 150 LOGGER.debug("DocumentBuilderFactory is: " + dbf.getClass().getName()); 151 } catch (FactoryConfigurationError fce) { 152 Exception e = fce.getException(); 153 LOGGER.debug("Could not instantiate a DocumentBuilderFactory.", e); 154 throw fce; 155 } 156 157 try { 158 dbf.setValidating(true); 159 160 DocumentBuilder docBuilder = dbf.newDocumentBuilder(); 161 162 docBuilder.setErrorHandler(new SAXErrorHandler()); 163 docBuilder.setEntityResolver(new Log4jEntityResolver()); 164 165 Document doc = action.parse(docBuilder); 166 parse(doc.getDocumentElement()); 167 } catch (Exception e) { 168 if (e instanceof InterruptedException || e instanceof InterruptedIOException) { 169 Thread.currentThread().interrupt(); 170 } 171 // I know this is miserable... 172 LOGGER.error("Could not parse " + action.toString() + ".", e); 173 } 174 } 175 176 @Override 177 public Configuration reconfigure() { 178 try { 179 final ConfigurationSource source = getConfigurationSource().resetInputStream(); 180 if (source == null) { 181 return null; 182 } 183 final XmlConfigurationFactory factory = new XmlConfigurationFactory(); 184 final XmlConfiguration config = 185 (XmlConfiguration) factory.getConfiguration(getLoggerContext(), source); 186 return config == null || config.getState() != State.INITIALIZING ? null : config; 187 } catch (final IOException ex) { 188 LOGGER.error("Cannot locate file {}: {}", getConfigurationSource(), ex); 189 } 190 return null; 191 } 192 193 /** 194 * Delegates unrecognized content to created instance if it supports UnrecognizedElementParser. 195 * 196 * @param instance instance, may be null. 197 * @param element element, may not be null. 198 * @param props properties 199 * @throws IOException thrown if configuration of owner object should be abandoned. 200 */ 201 private void parseUnrecognizedElement(final Object instance, final Element element, 202 final Properties props) throws Exception { 203 boolean recognized = false; 204 if (instance instanceof UnrecognizedElementHandler) { 205 recognized = ((UnrecognizedElementHandler) instance).parseUnrecognizedElement( 206 element, props); 207 } 208 if (!recognized) { 209 LOGGER.warn("Unrecognized element {}", element.getNodeName()); 210 } 211 } 212 213 /** 214 * Delegates unrecognized content to created instance if 215 * it supports UnrecognizedElementParser and catches and 216 * logs any exception. 217 * 218 * @param instance instance, may be null. 219 * @param element element, may not be null. 220 * @param props properties 221 * @since 1.2.15 222 */ 223 private void quietParseUnrecognizedElement(final Object instance, 224 final Element element, 225 final Properties props) { 226 try { 227 parseUnrecognizedElement(instance, element, props); 228 } catch (Exception ex) { 229 if (ex instanceof InterruptedException || ex instanceof InterruptedIOException) { 230 Thread.currentThread().interrupt(); 231 } 232 LOGGER.error("Error in extension content: ", ex); 233 } 234 } 235 236 /** 237 * Substitutes property value for any references in expression. 238 * 239 * @param value value from configuration file, may contain 240 * literal text, property references or both 241 * @param props properties. 242 * @return evaluated expression, may still contain expressions 243 * if unable to expand. 244 */ 245 public String subst(final String value, final Properties props) { 246 try { 247 return OptionConverter.substVars(value, props); 248 } catch (IllegalArgumentException e) { 249 LOGGER.warn("Could not perform variable substitution.", e); 250 return value; 251 } 252 } 253 254 /** 255 * Sets a parameter based from configuration file content. 256 * 257 * @param elem param element, may not be null. 258 * @param propSetter property setter, may not be null. 259 * @param props properties 260 * @since 1.2.15 261 */ 262 public void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) { 263 String name = subst(elem.getAttribute("name"), props); 264 String value = (elem.getAttribute("value")); 265 value = subst(OptionConverter.convertSpecialChars(value), props); 266 propSetter.setProperty(name, value); 267 } 268 269 /** 270 * Creates an object and processes any nested param elements 271 * but does not call activateOptions. If the class also supports 272 * UnrecognizedElementParser, the parseUnrecognizedElement method 273 * will be call for any child elements other than param. 274 * 275 * @param element element, may not be null. 276 * @param props properties 277 * @param expectedClass interface or class expected to be implemented 278 * by created class 279 * @return created class or null. 280 * @throws Exception thrown if the contain object should be abandoned. 281 * @since 1.2.15 282 */ 283 public Object parseElement(final Element element, final Properties props, 284 @SuppressWarnings("rawtypes") final Class expectedClass) throws Exception { 285 String clazz = subst(element.getAttribute("class"), props); 286 Object instance = OptionConverter.instantiateByClassName(clazz, 287 expectedClass, null); 288 289 if (instance != null) { 290 PropertySetter propSetter = new PropertySetter(instance); 291 NodeList children = element.getChildNodes(); 292 final int length = children.getLength(); 293 294 for (int loop = 0; loop < length; loop++) { 295 Node currentNode = children.item(loop); 296 if (currentNode.getNodeType() == Node.ELEMENT_NODE) { 297 Element currentElement = (Element) currentNode; 298 String tagName = currentElement.getTagName(); 299 if (tagName.equals("param")) { 300 setParameter(currentElement, propSetter, props); 301 } else { 302 parseUnrecognizedElement(instance, currentElement, props); 303 } 304 } 305 } 306 return instance; 307 } 308 return null; 309 } 310 311 /** 312 * Used internally to parse appenders by IDREF name. 313 */ 314 private Appender findAppenderByName(Document doc, String appenderName) { 315 Appender appender = appenderMap.get(appenderName); 316 317 if (appender != null) { 318 return appender; 319 } 320 // Endre's hack: 321 Element element = null; 322 NodeList list = doc.getElementsByTagName("appender"); 323 for (int t = 0; t < list.getLength(); t++) { 324 Node node = list.item(t); 325 NamedNodeMap map = node.getAttributes(); 326 Node attrNode = map.getNamedItem("name"); 327 if (appenderName.equals(attrNode.getNodeValue())) { 328 element = (Element) node; 329 break; 330 } 331 } 332 // Hack finished. 333 334 if (element == null) { 335 336 LOGGER.error("No appender named [{}] could be found.", appenderName); 337 return null; 338 } 339 appender = parseAppender(element); 340 if (appender != null) { 341 appenderMap.put(appenderName, appender); 342 } 343 return appender; 344 } 345 346 /** 347 * Used internally to parse appenders by IDREF element. 348 */ 349 public Appender findAppenderByReference(Element appenderRef) { 350 String appenderName = subst(appenderRef.getAttribute(REF_ATTR)); 351 Document doc = appenderRef.getOwnerDocument(); 352 return findAppenderByName(doc, appenderName); 353 } 354 355 /** 356 * Used internally to parse an appender element. 357 */ 358 public Appender parseAppender(Element appenderElement) { 359 String className = subst(appenderElement.getAttribute(CLASS_ATTR)); 360 LOGGER.debug("Class name: [" + className + ']'); 361 Appender appender = manager.parseAppender(className, appenderElement, this); 362 if (appender == null) { 363 appender = buildAppender(className, appenderElement); 364 } 365 return appender; 366 } 367 368 private Appender buildAppender(String className, Element appenderElement) { 369 try { 370 Appender appender = LoaderUtil.newInstanceOf(className); 371 PropertySetter propSetter = new PropertySetter(appender); 372 373 appender.setName(subst(appenderElement.getAttribute(NAME_ATTR))); 374 forEachElement(appenderElement.getChildNodes(), currentElement -> { 375 // Parse appender parameters 376 switch (currentElement.getTagName()) { 377 case PARAM_TAG: 378 setParameter(currentElement, propSetter); 379 break; 380 case LAYOUT_TAG: 381 appender.setLayout(parseLayout(currentElement)); 382 break; 383 case FILTER_TAG: 384 Filter filter = parseFilters(currentElement); 385 if (filter != null) { 386 LOGGER.debug("Adding filter of type [{}] to appender named [{}]", 387 filter.getClass(), appender.getName()); 388 appender.addFilter(filter); 389 } 390 break; 391 case ERROR_HANDLER_TAG: 392 parseErrorHandler(currentElement, appender); 393 break; 394 case APPENDER_REF_TAG: 395 String refName = subst(currentElement.getAttribute(REF_ATTR)); 396 if (appender instanceof AppenderAttachable) { 397 AppenderAttachable aa = (AppenderAttachable) appender; 398 Appender child = findAppenderByReference(currentElement); 399 LOGGER.debug("Attaching appender named [{}] to appender named [{}].", refName, 400 appender.getName()); 401 aa.addAppender(child); 402 } else { 403 LOGGER.error("Requesting attachment of appender named [{}] to appender named [{}]" 404 + "which does not implement org.apache.log4j.spi.AppenderAttachable.", 405 refName, appender.getName()); 406 } 407 break; 408 default: 409 try { 410 parseUnrecognizedElement(appender, currentElement, props); 411 } catch (Exception ex) { 412 throw new ConsumerException(ex); 413 } 414 } 415 }); 416 propSetter.activate(); 417 return appender; 418 } catch (ConsumerException ex) { 419 Throwable t = ex.getCause(); 420 if (t instanceof InterruptedException || t instanceof InterruptedIOException) { 421 Thread.currentThread().interrupt(); 422 } 423 LOGGER.error("Could not create an Appender. Reported error follows.", t); 424 } catch (Exception oops) { 425 if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) { 426 Thread.currentThread().interrupt(); 427 } 428 LOGGER.error("Could not create an Appender. Reported error follows.", oops); 429 } 430 return null; 431 } 432 433 public RewritePolicy parseRewritePolicy(Element rewritePolicyElement) { 434 String className = subst(rewritePolicyElement.getAttribute(CLASS_ATTR)); 435 LOGGER.debug("Class name: [" + className + ']'); 436 RewritePolicy policy = manager.parseRewritePolicy(className, rewritePolicyElement, this); 437 if (policy == null) { 438 policy = buildRewritePolicy(className, rewritePolicyElement); 439 } 440 return policy; 441 } 442 443 private RewritePolicy buildRewritePolicy(String className, Element element) { 444 try { 445 RewritePolicy policy = LoaderUtil.newInstanceOf(className); 446 PropertySetter propSetter = new PropertySetter(policy); 447 448 forEachElement(element.getChildNodes(), currentElement -> { 449 if (currentElement.getTagName().equalsIgnoreCase(PARAM_TAG)) { 450 setParameter(currentElement, propSetter); 451 } 452 }); 453 propSetter.activate(); 454 return policy; 455 } catch (ConsumerException ex) { 456 Throwable t = ex.getCause(); 457 if (t instanceof InterruptedException || t instanceof InterruptedIOException) { 458 Thread.currentThread().interrupt(); 459 } 460 LOGGER.error("Could not create an RewritePolicy. Reported error follows.", t); 461 } catch (Exception oops) { 462 if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) { 463 Thread.currentThread().interrupt(); 464 } 465 LOGGER.error("Could not create an RewritePolicy. Reported error follows.", oops); 466 } 467 return null; 468 } 469 470 /** 471 * Used internally to parse an {@link ErrorHandler} element. 472 */ 473 private void parseErrorHandler(Element element, Appender appender) { 474 ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName( 475 subst(element.getAttribute(CLASS_ATTR)), 476 ErrorHandler.class, 477 null); 478 479 if (eh != null) { 480 eh.setAppender(appender); 481 482 PropertySetter propSetter = new PropertySetter(eh); 483 forEachElement(element.getChildNodes(), currentElement -> { 484 String tagName = currentElement.getTagName(); 485 if (tagName.equals(PARAM_TAG)) { 486 setParameter(currentElement, propSetter); 487 } 488 }); 489 propSetter.activate(); 490 appender.setErrorHandler(eh); 491 } 492 } 493 494 /** 495 * Used internally to parse a filter element. 496 */ 497 public Filter parseFilters(Element filterElement) { 498 String className = subst(filterElement.getAttribute(CLASS_ATTR)); 499 LOGGER.debug("Class name: [" + className + ']'); 500 Filter filter = manager.parseFilter(className, filterElement, this); 501 if (filter == null) { 502 PropertySetter propSetter = new PropertySetter(filter); 503 forEachElement(filterElement.getChildNodes(), currentElement -> { 504 String tagName = currentElement.getTagName(); 505 if (tagName.equals(PARAM_TAG)) { 506 setParameter(currentElement, propSetter); 507 } else { 508 quietParseUnrecognizedElement(filter, currentElement, props); 509 } 510 }); 511 propSetter.activate(); 512 } 513 return filter; 514 } 515 516 /** 517 * Used internally to parse an category element. 518 */ 519 private void parseCategory(Element loggerElement) { 520 // Create a new org.apache.log4j.Category object from the <category> element. 521 String catName = subst(loggerElement.getAttribute(NAME_ATTR)); 522 boolean additivity = OptionConverter.toBoolean(subst(loggerElement.getAttribute(ADDITIVITY_ATTR)), true); 523 LoggerConfig loggerConfig = getLogger(catName); 524 if (loggerConfig == null) { 525 loggerConfig = new LoggerConfig(catName, org.apache.logging.log4j.Level.ERROR, additivity); 526 addLogger(catName, loggerConfig); 527 } else { 528 loggerConfig.setAdditive(additivity); 529 } 530 parseChildrenOfLoggerElement(loggerElement, loggerConfig, false); 531 } 532 533 /** 534 * Used internally to parse the roor category element. 535 */ 536 private void parseRoot(Element rootElement) { 537 LoggerConfig root = getRootLogger(); 538 parseChildrenOfLoggerElement(rootElement, root, true); 539 } 540 541 /** 542 * Used internally to parse the children of a LoggerConfig element. 543 */ 544 private void parseChildrenOfLoggerElement(Element catElement, LoggerConfig loggerConfig, boolean isRoot) { 545 546 final PropertySetter propSetter = new PropertySetter(loggerConfig); 547 loggerConfig.getAppenderRefs().clear(); 548 forEachElement(catElement.getChildNodes(), currentElement -> { 549 switch (currentElement.getTagName()) { 550 case APPENDER_REF_TAG: { 551 Appender appender = findAppenderByReference(currentElement); 552 String refName = subst(currentElement.getAttribute(REF_ATTR)); 553 if (appender != null) { 554 LOGGER.debug("Adding appender named [{}] to loggerConfig [{}].", refName, 555 loggerConfig.getName()); 556 loggerConfig.addAppender(getAppender(refName), null, null); 557 } else { 558 LOGGER.debug("Appender named [{}] not found.", refName); 559 } 560 break; 561 } 562 case LEVEL_TAG: case PRIORITY_TAG: { 563 parseLevel(currentElement, loggerConfig, isRoot); 564 break; 565 } 566 case PARAM_TAG: { 567 setParameter(currentElement, propSetter); 568 break; 569 } 570 default: { 571 quietParseUnrecognizedElement(loggerConfig, currentElement, props); 572 } 573 } 574 }); 575 propSetter.activate(); 576 } 577 578 /** 579 * Used internally to parse a layout element. 580 */ 581 public Layout parseLayout(Element layoutElement) { 582 String className = subst(layoutElement.getAttribute(CLASS_ATTR)); 583 LOGGER.debug("Parsing layout of class: \"{}\"", className); 584 Layout layout = manager.parseLayout(className, layoutElement, this); 585 if (layout == null) { 586 layout = buildLayout(className, layoutElement); 587 } 588 return layout; 589 } 590 591 private Layout buildLayout(String className, Element layout_element) { 592 try { 593 Layout layout = LoaderUtil.newInstanceOf(className); 594 PropertySetter propSetter = new PropertySetter(layout); 595 forEachElement(layout_element.getChildNodes(), currentElement -> { 596 String tagName = currentElement.getTagName(); 597 if (tagName.equals(PARAM_TAG)) { 598 setParameter(currentElement, propSetter); 599 } else { 600 try { 601 parseUnrecognizedElement(layout, currentElement, props); 602 } catch (Exception ex) { 603 throw new ConsumerException(ex); 604 } 605 } 606 }); 607 608 propSetter.activate(); 609 return layout; 610 } catch (ConsumerException ce) { 611 Throwable cause = ce.getCause(); 612 if (cause instanceof InterruptedException || cause instanceof InterruptedIOException) { 613 Thread.currentThread().interrupt(); 614 } 615 LOGGER.error("Could not create the Layout. Reported error follows.", cause); 616 } catch (Exception oops) { 617 if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) { 618 Thread.currentThread().interrupt(); 619 } 620 LOGGER.error("Could not create the Layout. Reported error follows.", oops); 621 } 622 return null; 623 } 624 625 /** 626 * Used internally to parse a level element. 627 */ 628 private void parseLevel(Element element, LoggerConfig logger, boolean isRoot) { 629 String catName = logger.getName(); 630 if (isRoot) { 631 catName = "root"; 632 } 633 634 String priStr = subst(element.getAttribute(VALUE_ATTR)); 635 LOGGER.debug("Level value for {} is [{}].", catName, priStr); 636 637 if (INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) { 638 if (isRoot) { 639 LOGGER.error("Root level cannot be inherited. Ignoring directive."); 640 } else { 641 logger.setLevel(null); 642 } 643 } else { 644 String className = subst(element.getAttribute(CLASS_ATTR)); 645 if (EMPTY_STR.equals(className)) { 646 logger.setLevel(OptionConverter.convertLevel(priStr, org.apache.logging.log4j.Level.DEBUG)); 647 } else { 648 LOGGER.debug("Desired Level sub-class: [{}]", className); 649 try { 650 Class<?> clazz = LoaderUtil.loadClass(className); 651 Method toLevelMethod = clazz.getMethod("toLevel", ONE_STRING_PARAM); 652 Level pri = (Level) toLevelMethod.invoke(null, priStr); 653 logger.setLevel(OptionConverter.convertLevel(pri)); 654 } catch (Exception e) { 655 if (e instanceof InterruptedException || e instanceof InterruptedIOException) { 656 Thread.currentThread().interrupt(); 657 } 658 LOGGER.error("Could not create level [" + priStr + 659 "]. Reported error follows.", e); 660 return; 661 } 662 } 663 } 664 LOGGER.debug("{} level set to {}", catName, logger.getLevel()); 665 } 666 667 private void setParameter(Element elem, PropertySetter propSetter) { 668 String name = subst(elem.getAttribute(NAME_ATTR)); 669 String value = (elem.getAttribute(VALUE_ATTR)); 670 value = subst(OptionConverter.convertSpecialChars(value)); 671 propSetter.setProperty(name, value); 672 } 673 674 /** 675 * Used internally to configure the log4j framework by parsing a DOM 676 * tree of XML elements based on <a 677 * href="doc-files/log4j.dtd">log4j.dtd</a>. 678 */ 679 private void parse(Element element) { 680 String rootElementName = element.getTagName(); 681 682 if (!rootElementName.equals(CONFIGURATION_TAG)) { 683 if (rootElementName.equals(OLD_CONFIGURATION_TAG)) { 684 LOGGER.warn("The <" + OLD_CONFIGURATION_TAG + 685 "> element has been deprecated."); 686 LOGGER.warn("Use the <" + CONFIGURATION_TAG + "> element instead."); 687 } else { 688 LOGGER.error("DOM element is - not a <" + CONFIGURATION_TAG + "> element."); 689 return; 690 } 691 } 692 693 694 String debugAttrib = subst(element.getAttribute(INTERNAL_DEBUG_ATTR)); 695 696 LOGGER.debug("debug attribute= \"" + debugAttrib + "\"."); 697 // if the log4j.dtd is not specified in the XML file, then the 698 // "debug" attribute is returned as the empty string. 699 String status = "error"; 700 if (!debugAttrib.equals("") && !debugAttrib.equals("null")) { 701 status = OptionConverter.toBoolean(debugAttrib, true) ? "debug" : "error"; 702 703 } else { 704 LOGGER.debug("Ignoring " + INTERNAL_DEBUG_ATTR + " attribute."); 705 } 706 707 String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR)); 708 if (!confDebug.equals("") && !confDebug.equals("null")) { 709 LOGGER.warn("The \"" + CONFIG_DEBUG_ATTR + "\" attribute is deprecated."); 710 LOGGER.warn("Use the \"" + INTERNAL_DEBUG_ATTR + "\" attribute instead."); 711 status = OptionConverter.toBoolean(confDebug, true) ? "debug" : "error"; 712 } 713 714 final StatusConfiguration statusConfig = new StatusConfiguration().withStatus(status); 715 statusConfig.initialize(); 716 717 forEachElement(element.getChildNodes(), currentElement -> { 718 switch (currentElement.getTagName()) { 719 case CATEGORY: 720 case LOGGER_ELEMENT: 721 parseCategory(currentElement); 722 break; 723 case ROOT_TAG: 724 parseRoot(currentElement); 725 break; 726 case RENDERER_TAG: 727 LOGGER.warn("Renderers are not supported by Log4j 2 and will be ignored."); 728 break; 729 case THROWABLE_RENDERER_TAG: 730 LOGGER.warn("Throwable Renderers are not supported by Log4j 2 and will be ignored."); 731 break; 732 case CATEGORY_FACTORY_TAG: 733 case LOGGER_FACTORY_TAG: 734 LOGGER.warn("Log4j 1 Logger factories are not supported by Log4j 2 and will be ignored."); 735 break; 736 case APPENDER_TAG: 737 Appender appender = parseAppender(currentElement); 738 appenderMap.put(appender.getName(), appender); 739 if (appender instanceof AppenderWrapper) { 740 addAppender(((AppenderWrapper) appender).getAppender()); 741 } else { 742 addAppender(new AppenderAdapter(appender).getAdapter()); 743 } 744 break; 745 default: 746 quietParseUnrecognizedElement(null, currentElement, props); 747 } 748 }); 749 } 750 751 private String subst(final String value) { 752 return getStrSubstitutor().replace(value); 753 } 754 755 public static void forEachElement(NodeList list, Consumer<Element> consumer) { 756 final int length = list.getLength(); 757 for (int loop = 0; loop < length; loop++) { 758 Node currentNode = list.item(loop); 759 760 if (currentNode.getNodeType() == Node.ELEMENT_NODE) { 761 Element currentElement = (Element) currentNode; 762 consumer.accept(currentElement); 763 } 764 } 765 } 766 767 private interface ParseAction { 768 Document parse(final DocumentBuilder parser) throws SAXException, IOException; 769 } 770 771 private static class SAXErrorHandler implements org.xml.sax.ErrorHandler { 772 private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger(); 773 774 @Override 775 public void error(final SAXParseException ex) { 776 emitMessage("Continuable parsing error ", ex); 777 } 778 779 @Override 780 public void fatalError(final SAXParseException ex) { 781 emitMessage("Fatal parsing error ", ex); 782 } 783 784 @Override 785 public void warning(final SAXParseException ex) { 786 emitMessage("Parsing warning ", ex); 787 } 788 789 private static void emitMessage(final String msg, final SAXParseException ex) { 790 LOGGER.warn("{} {} and column {}", msg, ex.getLineNumber(), ex.getColumnNumber()); 791 LOGGER.warn(ex.getMessage(), ex.getException()); 792 } 793 } 794 795 private static class ConsumerException extends RuntimeException { 796 797 ConsumerException(Exception ex) { 798 super(ex); 799 } 800 } 801}