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 */ 017 package org.apache.logging.log4j.core.config; 018 019 import org.apache.logging.log4j.Level; 020 import org.apache.logging.log4j.Logger; 021 import org.apache.logging.log4j.core.Appender; 022 import org.apache.logging.log4j.core.Filter; 023 import org.apache.logging.log4j.core.Layout; 024 import org.apache.logging.log4j.core.LogEvent; 025 import org.apache.logging.log4j.core.appender.ConsoleAppender; 026 import org.apache.logging.log4j.core.config.plugins.PluginAttr; 027 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 028 import org.apache.logging.log4j.core.config.plugins.PluginFactory; 029 import org.apache.logging.log4j.core.config.plugins.PluginManager; 030 import org.apache.logging.log4j.core.config.plugins.PluginElement; 031 import org.apache.logging.log4j.core.config.plugins.PluginNode; 032 import org.apache.logging.log4j.core.config.plugins.PluginType; 033 import org.apache.logging.log4j.core.config.plugins.PluginValue; 034 import org.apache.logging.log4j.core.filter.AbstractFilterable; 035 import org.apache.logging.log4j.core.helpers.NameUtil; 036 import org.apache.logging.log4j.core.layout.PatternLayout; 037 import org.apache.logging.log4j.core.lookup.Interpolator; 038 import org.apache.logging.log4j.core.lookup.MapLookup; 039 import org.apache.logging.log4j.core.lookup.StrLookup; 040 import org.apache.logging.log4j.core.lookup.StrSubstitutor; 041 import org.apache.logging.log4j.status.StatusLogger; 042 import org.apache.logging.log4j.util.PropertiesUtil; 043 044 import java.lang.annotation.Annotation; 045 import java.lang.reflect.Array; 046 import java.lang.reflect.Method; 047 import java.lang.reflect.Modifier; 048 import java.util.ArrayList; 049 import java.util.Collections; 050 import java.util.List; 051 import java.util.Map; 052 import java.util.concurrent.ConcurrentHashMap; 053 import java.util.concurrent.ConcurrentMap; 054 import java.util.concurrent.CopyOnWriteArrayList; 055 056 /** 057 * The Base Configuration. Many configuration implementations will extend this class. 058 */ 059 public class BaseConfiguration extends AbstractFilterable implements Configuration { 060 /** 061 * Allow subclasses access to the status logger without creating another instance. 062 */ 063 protected static final Logger LOGGER = StatusLogger.getLogger(); 064 065 /** 066 * The root node of the configuration. 067 */ 068 protected Node rootNode; 069 070 /** 071 * The Plugin Manager. 072 */ 073 protected PluginManager pluginManager; 074 075 /** 076 * Listeners for configuration changes. 077 */ 078 protected final List<ConfigurationListener> listeners = 079 new CopyOnWriteArrayList<ConfigurationListener>(); 080 081 /** 082 * The ConfigurationMonitor that checks for configuration changes. 083 */ 084 protected ConfigurationMonitor monitor = new DefaultConfigurationMonitor(); 085 086 private String name; 087 088 private ConcurrentMap<String, Appender<?>> appenders = new ConcurrentHashMap<String, Appender<?>>(); 089 090 private ConcurrentMap<String, LoggerConfig> loggers = new ConcurrentHashMap<String, LoggerConfig>(); 091 092 private final StrLookup tempLookup = new Interpolator(); 093 094 private final StrSubstitutor subst = new StrSubstitutor(tempLookup); 095 096 private LoggerConfig root = new LoggerConfig(); 097 098 private final boolean started = false; 099 100 private final ConcurrentMap<String, Object> componentMap = new ConcurrentHashMap<String, Object>(); 101 102 /** 103 * Constructor. 104 */ 105 protected BaseConfiguration() { 106 pluginManager = new PluginManager("Core"); 107 rootNode = new Node(); 108 } 109 110 public Map<String, String> getProperties() { 111 return (Map<String, String>) componentMap.get(CONTEXT_PROPERTIES); 112 } 113 114 /** 115 * Initialize the configuration. 116 */ 117 public void start() { 118 pluginManager.collectPlugins(); 119 setup(); 120 doConfigure(); 121 for (final LoggerConfig logger : loggers.values()) { 122 logger.startFilter(); 123 } 124 for (final Appender appender : appenders.values()) { 125 appender.start(); 126 } 127 128 startFilter(); 129 } 130 131 /** 132 * Tear down the configuration. 133 */ 134 public void stop() { 135 for (final LoggerConfig logger : loggers.values()) { 136 logger.clearAppenders(); 137 logger.stopFilter(); 138 } 139 // Stop the appenders in reverse order in case they still have activity. 140 final Appender[] array = appenders.values().toArray(new Appender[appenders.size()]); 141 for (int i = array.length - 1; i >= 0; --i) { 142 array[i].stop(); 143 } 144 stopFilter(); 145 } 146 147 protected void setup() { 148 } 149 150 public Object getComponent(final String name) { 151 return componentMap.get(name); 152 } 153 154 public void addComponent(final String name, final Object obj) { 155 componentMap.putIfAbsent(name, obj); 156 } 157 158 protected void doConfigure() { 159 boolean setRoot = false; 160 boolean setLoggers = false; 161 for (final Node child : rootNode.getChildren()) { 162 createConfiguration(child, null); 163 if (child.getObject() == null) { 164 continue; 165 } 166 if (child.getName().equalsIgnoreCase("properties")) { 167 if (tempLookup == subst.getVariableResolver()) { 168 subst.setVariableResolver((StrLookup) child.getObject()); 169 } else { 170 LOGGER.error("Properties declaration must be the first element in the configuration"); 171 } 172 continue; 173 } else if (tempLookup == subst.getVariableResolver()) { 174 final Map<String, String> map = (Map<String, String>) componentMap.get(CONTEXT_PROPERTIES); 175 final StrLookup lookup = map == null ? null : new MapLookup(map); 176 subst.setVariableResolver(new Interpolator(lookup)); 177 } 178 if (child.getName().equalsIgnoreCase("appenders")) { 179 appenders = (ConcurrentMap<String, Appender<?>>) child.getObject(); 180 } else if (child.getObject() instanceof Filter) { 181 addFilter((Filter) child.getObject()); 182 } else if (child.getName().equalsIgnoreCase("loggers")) { 183 final Loggers l = (Loggers) child.getObject(); 184 loggers = l.getMap(); 185 setLoggers = true; 186 if (l.getRoot() != null) { 187 root = l.getRoot(); 188 setRoot = true; 189 } 190 } else { 191 LOGGER.error("Unknown object \"" + child.getName() + "\" of type " + 192 child.getObject().getClass().getName() + " is ignored"); 193 } 194 } 195 196 if (!setLoggers) { 197 LOGGER.warn("No Loggers were configured, using default. Is the Loggers element missing?"); 198 setToDefault(); 199 return; 200 } else if (!setRoot) { 201 LOGGER.warn("No Root logger was configured, using default"); 202 setToDefault(); 203 return; 204 } 205 206 for (final Map.Entry<String, LoggerConfig> entry : loggers.entrySet()) { 207 final LoggerConfig l = entry.getValue(); 208 for (final AppenderRef ref : l.getAppenderRefs()) { 209 final Appender app = appenders.get(ref.getRef()); 210 if (app != null) { 211 l.addAppender(app, ref.getLevel(), ref.getFilter()); 212 } else { 213 LOGGER.error("Unable to locate appender " + ref.getRef() + " for logger " + l.getName()); 214 } 215 } 216 217 } 218 219 setParents(); 220 } 221 222 private void setToDefault() { 223 setName(DefaultConfiguration.DEFAULT_NAME); 224 final Layout layout = PatternLayout.createLayout("%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n", 225 null, null, null); 226 final Appender appender = ConsoleAppender.createAppender(layout, null, "SYSTEM_OUT", "Console", "false", 227 "true"); 228 appender.start(); 229 addAppender(appender); 230 final LoggerConfig root = getRootLogger(); 231 root.addAppender(appender, null, null); 232 233 final String levelName = PropertiesUtil.getProperties().getStringProperty(DefaultConfiguration.DEFAULT_LEVEL); 234 final Level level = levelName != null && Level.valueOf(levelName) != null ? 235 Level.valueOf(levelName) : Level.ERROR; 236 root.setLevel(level); 237 } 238 239 protected PluginManager getPluginManager() { 240 return pluginManager; 241 } 242 243 /** 244 * Set the name of the configuration. 245 * @param name The name. 246 */ 247 public void setName(final String name) { 248 this.name = name; 249 } 250 251 /** 252 * Returns the name of the configuration. 253 * @return the name of the configuration. 254 */ 255 public String getName() { 256 return name; 257 } 258 259 /** 260 * Add a listener for changes on the configuration. 261 * @param listener The ConfigurationListener to add. 262 */ 263 public void addListener(final ConfigurationListener listener) { 264 listeners.add(listener); 265 } 266 267 /** 268 * Remove a ConfigurationListener. 269 * @param listener The ConfigurationListener to remove. 270 */ 271 public void removeListener(final ConfigurationListener listener) { 272 listeners.remove(listener); 273 } 274 275 /** 276 * Returns the Appender with the specified name. 277 * @param name The name of the Appender. 278 * @return the Appender with the specified name or null if the Appender cannot be located. 279 */ 280 public Appender getAppender(final String name) { 281 return appenders.get(name); 282 } 283 284 /** 285 * Returns a Map containing all the Appenders and their name. 286 * @return A Map containing each Appender's name and the Appender object. 287 */ 288 public Map<String, Appender<?>> getAppenders() { 289 return appenders; 290 } 291 292 /** 293 * Adds an Appender to the configuration. 294 * @param appender The Appender to add. 295 */ 296 public void addAppender(final Appender appender) { 297 appenders.put(appender.getName(), appender); 298 } 299 300 public StrSubstitutor getSubst() { 301 return subst; 302 } 303 304 public ConfigurationMonitor getConfigurationMonitor() { 305 return monitor; 306 } 307 308 /** 309 * Associates an Appender with a LoggerConfig. This method is synchronized in case a Logger with the 310 * same name is being updated at the same time. 311 * 312 * Note: This method is not used when configuring via configuration. It is primarily used by 313 * unit tests. 314 * @param logger The Logger the Appender will be associated with. 315 * @param appender The Appender. 316 */ 317 public synchronized void addLoggerAppender(final org.apache.logging.log4j.core.Logger logger, 318 final Appender appender) { 319 final String name = logger.getName(); 320 appenders.putIfAbsent(name, appender); 321 final LoggerConfig lc = getLoggerConfig(name); 322 if (lc.getName().equals(name)) { 323 lc.addAppender(appender, null, null); 324 } else { 325 final LoggerConfig nlc = new LoggerConfig(name, lc.getLevel(), lc.isAdditive()); 326 nlc.addAppender(appender, null, null); 327 nlc.setParent(lc); 328 loggers.putIfAbsent(name, nlc); 329 setParents(); 330 logger.getContext().updateLoggers(); 331 } 332 } 333 /** 334 * Associates a Filter with a LoggerConfig. This method is synchronized in case a Logger with the 335 * same name is being updated at the same time. 336 * 337 * Note: This method is not used when configuring via configuration. It is primarily used by 338 * unit tests. 339 * @param logger The Logger the Fo;ter will be associated with. 340 * @param filter The Filter. 341 */ 342 public synchronized void addLoggerFilter(final org.apache.logging.log4j.core.Logger logger, final Filter filter) { 343 final String name = logger.getName(); 344 final LoggerConfig lc = getLoggerConfig(name); 345 if (lc.getName().equals(name)) { 346 347 lc.addFilter(filter); 348 } else { 349 final LoggerConfig nlc = new LoggerConfig(name, lc.getLevel(), lc.isAdditive()); 350 nlc.addFilter(filter); 351 nlc.setParent(lc); 352 loggers.putIfAbsent(name, nlc); 353 setParents(); 354 logger.getContext().updateLoggers(); 355 } 356 } 357 /** 358 * Marks a LoggerConfig as additive. This method is synchronized in case a Logger with the 359 * same name is being updated at the same time. 360 * 361 * Note: This method is not used when configuring via configuration. It is primarily used by 362 * unit tests. 363 * @param logger The Logger the Appender will be associated with. 364 * @param additive True if the LoggerConfig should be additive, false otherwise. 365 */ 366 public synchronized void setLoggerAdditive(final org.apache.logging.log4j.core.Logger logger, 367 final boolean additive) { 368 final String name = logger.getName(); 369 final LoggerConfig lc = getLoggerConfig(name); 370 if (lc.getName().equals(name)) { 371 lc.setAdditive(additive); 372 } else { 373 final LoggerConfig nlc = new LoggerConfig(name, lc.getLevel(), additive); 374 nlc.setParent(lc); 375 loggers.putIfAbsent(name, nlc); 376 setParents(); 377 logger.getContext().updateLoggers(); 378 } 379 } 380 381 /** 382 * Remove an Appender. First removes any associations between LoggerContigs and the Appender, removes 383 * the Appender from this appender list and then stops the appender. This method is synchronized in 384 * case an Appender with the same name is being added during the removal. 385 * @param name the name of the appender to remove. 386 */ 387 public synchronized void removeAppender(final String name) { 388 for (final LoggerConfig logger : loggers.values()) { 389 logger.removeAppender(name); 390 } 391 final Appender app = appenders.remove(name); 392 393 if (app != null) { 394 app.stop(); 395 } 396 } 397 398 /** 399 * Locates the appropriate LoggerConfig for a Logger name. This will remove tokens from the 400 * package name as necessary or return the root LoggerConfig if no other matches were found. 401 * @param name The Logger name. 402 * @return The located LoggerConfig. 403 */ 404 public LoggerConfig getLoggerConfig(final String name) { 405 if (loggers.containsKey(name)) { 406 return loggers.get(name); 407 } 408 String substr = name; 409 while ((substr = NameUtil.getSubName(substr)) != null) { 410 if (loggers.containsKey(substr)) { 411 return loggers.get(substr); 412 } 413 } 414 return root; 415 } 416 417 /** 418 * Returns the root Logger. 419 * @return the root Logger. 420 */ 421 public LoggerConfig getRootLogger() { 422 return root; 423 } 424 425 /** 426 * Returns a Map of all the LoggerConfigs. 427 * @return a Map with each entry containing the name of the Logger and the LoggerConfig. 428 */ 429 public Map<String, LoggerConfig> getLoggers() { 430 return Collections.unmodifiableMap(loggers); 431 } 432 433 /** 434 * Returns the LoggerConfig with the specified name. 435 * @param name The Logger name. 436 * @return The LoggerConfig or null if no match was found. 437 */ 438 public LoggerConfig getLogger(final String name) { 439 return loggers.get(name); 440 } 441 442 /** 443 * Adding a logger cannot be done atomically so is not allowed in an active configuration. Adding 444 * or removing a Logger requires creating a new configuration and then switching. 445 * 446 * @param name The name of the Logger. 447 * @param loggerConfig The LoggerConfig. 448 */ 449 public void addLogger(final String name, final LoggerConfig loggerConfig) { 450 if (started) { 451 final String msg = "Cannot add logger " + name + " to an active configuration"; 452 LOGGER.warn(msg); 453 throw new IllegalStateException(msg); 454 } 455 loggers.put(name, loggerConfig); 456 setParents(); 457 } 458 459 /** 460 * Removing a logger cannot be done atomically so is not allowed in an active configuration. Adding 461 * or removing a Logger requires creating a new configuration and then switching. 462 * 463 * @param name The name of the Logger. 464 */ 465 public void removeLogger(final String name) { 466 if (started) { 467 final String msg = "Cannot remove logger " + name + " in an active configuration"; 468 LOGGER.warn(msg); 469 throw new IllegalStateException(msg); 470 } 471 loggers.remove(name); 472 setParents(); 473 } 474 475 public void createConfiguration(final Node node, final LogEvent event) { 476 final PluginType type = node.getType(); 477 if (type != null && type.isDeferChildren()) { 478 node.setObject(createPluginObject(type, node, event)); 479 } else { 480 for (final Node child : node.getChildren()) { 481 createConfiguration(child, event); 482 } 483 484 if (type == null) { 485 if (node.getParent() != null) { 486 LOGGER.error("Unable to locate plugin for " + node.getName()); 487 } 488 } else { 489 node.setObject(createPluginObject(type, node, event)); 490 } 491 } 492 } 493 494 /* 495 * Retrieve a static public 'method to create the desired object. Every parameter 496 * will be annotated to identify the appropriate attribute or element to use to 497 * set the value of the parameter. 498 * Parameters annotated with PluginAttr will always be set as Strings. 499 * Parameters annotated with PluginElement may be Objects or arrays. Collections 500 * and Maps are currently not supported, although the factory method that is called 501 * can create these from an array. 502 * 503 * Although the happy path works, more work still needs to be done to log incorrect 504 * parameters. These will generally result in unhelpful InvocationTargetExceptions. 505 * @param classClass the class. 506 * @return the instantiate method or null if there is none by that 507 * description. 508 */ 509 private Object createPluginObject(final PluginType type, final Node node, final LogEvent event) 510 { 511 final Class clazz = type.getPluginClass(); 512 513 if (Map.class.isAssignableFrom(clazz)) { 514 try { 515 final Map<String, Object> map = (Map<String, Object>) clazz.newInstance(); 516 for (final Node child : node.getChildren()) { 517 map.put(child.getName(), child.getObject()); 518 } 519 return map; 520 } catch (final Exception ex) { 521 LOGGER.warn("Unable to create Map for " + type.getElementName() + " of class " + 522 clazz); 523 } 524 } 525 526 if (List.class.isAssignableFrom(clazz)) { 527 try { 528 final List<Object> list = (List<Object>) clazz.newInstance(); 529 for (final Node child : node.getChildren()) { 530 list.add(child.getObject()); 531 } 532 return list; 533 } catch (final Exception ex) { 534 LOGGER.warn("Unable to create List for " + type.getElementName() + " of class " + 535 clazz); 536 } 537 } 538 539 Method factoryMethod = null; 540 541 for (final Method method : clazz.getMethods()) { 542 if (method.isAnnotationPresent(PluginFactory.class)) { 543 factoryMethod = method; 544 break; 545 } 546 } 547 if (factoryMethod == null) { 548 return null; 549 } 550 551 final Annotation[][] parmArray = factoryMethod.getParameterAnnotations(); 552 final Class[] parmClasses = factoryMethod.getParameterTypes(); 553 if (parmArray.length != parmClasses.length) { 554 LOGGER.error("Number of parameter annotations does not equal the number of paramters"); 555 } 556 final Object[] parms = new Object[parmClasses.length]; 557 558 int index = 0; 559 final Map<String, String> attrs = node.getAttributes(); 560 final List<Node> children = node.getChildren(); 561 final StringBuilder sb = new StringBuilder(); 562 final List<Node> used = new ArrayList<Node>(); 563 564 /* 565 * For each parameter: 566 * If the parameter is an attribute store the value of the attribute in the parameter array. 567 * If the parameter is an element: 568 * Determine if the required parameter is an array. 569 * If so, if a child contains the array, use it, 570 * otherwise create the array from all child nodes of the correct type. 571 * Store the array into the parameter array. 572 * If not an array, store the object in the child node into the parameter array. 573 */ 574 for (final Annotation[] parmTypes : parmArray) { 575 for (final Annotation a : parmTypes) { 576 if (sb.length() == 0) { 577 sb.append(" with params("); 578 } else { 579 sb.append(", "); 580 } 581 if (a instanceof PluginNode) { 582 parms[index] = node; 583 sb.append("Node=").append(node.getName()); 584 } else if (a instanceof PluginConfiguration) { 585 parms[index] = this; 586 if (this.name != null) { 587 sb.append("Configuration(").append(name).append(")"); 588 } else { 589 sb.append("Configuration"); 590 } 591 } else if (a instanceof PluginValue) { 592 final String name = ((PluginValue) a).value(); 593 String v = node.getValue(); 594 if (v == null) { 595 v = getAttrValue("value", attrs); 596 } 597 final String value = subst.replace(event, v); 598 sb.append(name).append("=\"").append(value).append("\""); 599 parms[index] = value; 600 } else if (a instanceof PluginAttr) { 601 final String name = ((PluginAttr) a).value(); 602 final String value = subst.replace(event, getAttrValue(name, attrs)); 603 sb.append(name).append("=\"").append(value).append("\""); 604 parms[index] = value; 605 } else if (a instanceof PluginElement) { 606 final PluginElement elem = (PluginElement) a; 607 final String name = elem.value(); 608 if (parmClasses[index].isArray()) { 609 final Class parmClass = parmClasses[index].getComponentType(); 610 final List<Object> list = new ArrayList<Object>(); 611 sb.append(name).append("={"); 612 boolean first = true; 613 for (final Node child : children) { 614 final PluginType childType = child.getType(); 615 if (elem.value().equalsIgnoreCase(childType.getElementName()) || 616 parmClass.isAssignableFrom(childType.getPluginClass())) { 617 used.add(child); 618 if (!first) { 619 sb.append(", "); 620 } 621 first = false; 622 final Object obj = child.getObject(); 623 if (obj == null) { 624 LOGGER.error("Null object returned for " + child.getName() + " in " + 625 node.getName()); 626 continue; 627 } 628 if (obj.getClass().isArray()) { 629 printArray(sb, (Object[]) obj); 630 parms[index] = obj; 631 break; 632 } 633 sb.append(child.toString()); 634 list.add(obj); 635 } 636 } 637 sb.append("}"); 638 if (parms[index] != null) { 639 break; 640 } 641 if (list.size() > 0 && !parmClass.isAssignableFrom(list.get(0).getClass())) { 642 LOGGER.error("Attempted to assign List containing class " + 643 list.get(0).getClass().getName() + " to array of type " + parmClass + 644 " for attribute " + name); 645 break; 646 } 647 final Object[] array = (Object[]) Array.newInstance(parmClass, list.size()); 648 int i = 0; 649 for (final Object obj : list) { 650 array[i] = obj; 651 ++i; 652 } 653 parms[index] = array; 654 } else { 655 final Class parmClass = parmClasses[index]; 656 boolean present = false; 657 for (final Node child : children) { 658 final PluginType childType = child.getType(); 659 if (elem.value().equals(childType.getElementName()) || 660 parmClass.isAssignableFrom(childType.getPluginClass())) { 661 sb.append(child.getName()).append("(").append(child.toString()).append(")"); 662 present = true; 663 used.add(child); 664 parms[index] = child.getObject(); 665 break; 666 } 667 } 668 if (!present) { 669 sb.append("null"); 670 } 671 } 672 } 673 } 674 ++index; 675 } 676 if (sb.length() > 0) { 677 sb.append(")"); 678 } 679 680 if (attrs.size() > 0) { 681 final StringBuilder eb = new StringBuilder(); 682 for (final String key : attrs.keySet()) { 683 if (eb.length() == 0) { 684 eb.append(node.getName()); 685 eb.append(" contains "); 686 if (attrs.size() == 1) { 687 eb.append("an invalid element or attribute "); 688 } else { 689 eb.append("invalid attributes "); 690 } 691 } else { 692 eb.append(", "); 693 } 694 eb.append("\""); 695 eb.append(key); 696 eb.append("\""); 697 698 } 699 LOGGER.error(eb.toString()); 700 } 701 702 if (!type.isDeferChildren() && used.size() != children.size()) { 703 for (final Node child : children) { 704 if (used.contains(child)) { 705 continue; 706 } 707 final String nodeType = node.getType().getElementName(); 708 final String start = nodeType.equals(node.getName()) ? node.getName() : nodeType + " " + node.getName(); 709 LOGGER.error(start + " has no parameter that matches element " + child.getName()); 710 } 711 } 712 713 try { 714 final int mod = factoryMethod.getModifiers(); 715 if (!Modifier.isStatic(mod)) { 716 LOGGER.error(factoryMethod.getName() + " method is not static on class " + 717 clazz.getName() + " for element " + node.getName()); 718 return null; 719 } 720 LOGGER.debug("Calling {} on class {} for element {}{}", factoryMethod.getName(), clazz.getName(), 721 node.getName(), sb.toString()); 722 //if (parms.length > 0) { 723 return factoryMethod.invoke(null, parms); 724 //} 725 //return factoryMethod.invoke(null, node); 726 } catch (final Exception e) { 727 LOGGER.error("Unable to invoke method " + factoryMethod.getName() + " in class " + 728 clazz.getName() + " for element " + node.getName(), e); 729 } 730 return null; 731 } 732 733 private void printArray(final StringBuilder sb, final Object... array) { 734 boolean first = true; 735 for (final Object obj : array) { 736 if (!first) { 737 sb.append(", "); 738 } 739 sb.append(obj.toString()); 740 first = false; 741 } 742 } 743 744 private String getAttrValue(final String name, final Map<String, String> attrs) { 745 for (final String key : attrs.keySet()) { 746 if (key.equalsIgnoreCase(name)) { 747 final String attr = attrs.get(key); 748 attrs.remove(key); 749 return attr; 750 } 751 } 752 return null; 753 } 754 755 private void setParents() { 756 for (final Map.Entry<String, LoggerConfig> entry : loggers.entrySet()) { 757 final LoggerConfig logger = entry.getValue(); 758 String name = entry.getKey(); 759 if (!name.equals("")) { 760 final int i = name.lastIndexOf('.'); 761 if (i > 0) { 762 name = name.substring(0, i); 763 LoggerConfig parent = getLoggerConfig(name); 764 if (parent == null) { 765 parent = root; 766 } 767 logger.setParent(parent); 768 } 769 } 770 } 771 } 772 }