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.logging.log4j.core.config; 018 019import java.io.ByteArrayOutputStream; 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.Serializable; 023import java.lang.ref.WeakReference; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Collection; 027import java.util.Collections; 028import java.util.HashSet; 029import java.util.LinkedHashMap; 030import java.util.List; 031import java.util.Map; 032import java.util.Objects; 033import java.util.Set; 034import java.util.concurrent.ConcurrentHashMap; 035import java.util.concurrent.ConcurrentMap; 036import java.util.concurrent.CopyOnWriteArrayList; 037import java.util.concurrent.TimeUnit; 038 039import org.apache.logging.log4j.Level; 040import org.apache.logging.log4j.core.Appender; 041import org.apache.logging.log4j.core.Filter; 042import org.apache.logging.log4j.core.Layout; 043import org.apache.logging.log4j.core.LifeCycle2; 044import org.apache.logging.log4j.core.LogEvent; 045import org.apache.logging.log4j.core.LoggerContext; 046import org.apache.logging.log4j.core.Version; 047import org.apache.logging.log4j.core.appender.AsyncAppender; 048import org.apache.logging.log4j.core.appender.ConsoleAppender; 049import org.apache.logging.log4j.core.async.AsyncLoggerConfig; 050import org.apache.logging.log4j.core.async.AsyncLoggerConfigDelegate; 051import org.apache.logging.log4j.core.async.AsyncLoggerConfigDisruptor; 052import org.apache.logging.log4j.core.config.arbiters.Arbiter; 053import org.apache.logging.log4j.core.config.arbiters.SelectArbiter; 054import org.apache.logging.log4j.core.config.plugins.util.PluginBuilder; 055import org.apache.logging.log4j.core.config.plugins.util.PluginManager; 056import org.apache.logging.log4j.core.config.plugins.util.PluginType; 057import org.apache.logging.log4j.core.filter.AbstractFilterable; 058import org.apache.logging.log4j.core.layout.PatternLayout; 059import org.apache.logging.log4j.core.lookup.Interpolator; 060import org.apache.logging.log4j.core.lookup.MapLookup; 061import org.apache.logging.log4j.core.lookup.StrLookup; 062import org.apache.logging.log4j.core.lookup.StrSubstitutor; 063import org.apache.logging.log4j.core.net.Advertiser; 064import org.apache.logging.log4j.core.script.AbstractScript; 065import org.apache.logging.log4j.core.script.ScriptManager; 066import org.apache.logging.log4j.core.script.ScriptRef; 067import org.apache.logging.log4j.core.util.Constants; 068import org.apache.logging.log4j.core.util.DummyNanoClock; 069import org.apache.logging.log4j.core.util.Loader; 070import org.apache.logging.log4j.core.util.NameUtil; 071import org.apache.logging.log4j.core.util.NanoClock; 072import org.apache.logging.log4j.core.util.Source; 073import org.apache.logging.log4j.core.util.WatchManager; 074import org.apache.logging.log4j.core.util.Watcher; 075import org.apache.logging.log4j.core.util.WatcherFactory; 076import org.apache.logging.log4j.util.PropertiesUtil; 077 078/** 079 * The base Configuration. Many configuration implementations will extend this class. 080 */ 081public abstract class AbstractConfiguration extends AbstractFilterable implements Configuration { 082 083 private static final int BUF_SIZE = 16384; 084 085 /** 086 * The root node of the configuration. 087 */ 088 protected Node rootNode; 089 090 /** 091 * Listeners for configuration changes. 092 */ 093 protected final List<ConfigurationListener> listeners = new CopyOnWriteArrayList<>(); 094 095 /** 096 * Packages found in configuration "packages" attribute. 097 */ 098 protected final List<String> pluginPackages = new ArrayList<>(); 099 100 /** 101 * The plugin manager. 102 */ 103 protected PluginManager pluginManager; 104 105 /** 106 * Shutdown hook is enabled by default. 107 */ 108 protected boolean isShutdownHookEnabled = true; 109 110 /** 111 * Shutdown timeout in milliseconds. 112 */ 113 protected long shutdownTimeoutMillis = 0; 114 115 /** 116 * The Script manager. 117 */ 118 protected ScriptManager scriptManager; 119 120 /** 121 * The Advertiser which exposes appender configurations to external systems. 122 */ 123 private Advertiser advertiser = new DefaultAdvertiser(); 124 private Node advertiserNode = null; 125 private Object advertisement; 126 private String name; 127 private ConcurrentMap<String, Appender> appenders = new ConcurrentHashMap<>(); 128 private ConcurrentMap<String, LoggerConfig> loggerConfigs = new ConcurrentHashMap<>(); 129 private List<CustomLevelConfig> customLevels = Collections.emptyList(); 130 private final ConcurrentMap<String, String> propertyMap = new ConcurrentHashMap<>(); 131 private final StrLookup tempLookup = new Interpolator(propertyMap); 132 private final StrSubstitutor subst = new StrSubstitutor(tempLookup); 133 private LoggerConfig root = new LoggerConfig(); 134 private final ConcurrentMap<String, Object> componentMap = new ConcurrentHashMap<>(); 135 private final ConfigurationSource configurationSource; 136 private final ConfigurationScheduler configurationScheduler = new ConfigurationScheduler(); 137 private final WatchManager watchManager = new WatchManager(configurationScheduler); 138 private AsyncLoggerConfigDisruptor asyncLoggerConfigDisruptor; 139 private NanoClock nanoClock = new DummyNanoClock(); 140 private final WeakReference<LoggerContext> loggerContext; 141 142 /** 143 * Constructor. 144 */ 145 protected AbstractConfiguration(final LoggerContext loggerContext, final ConfigurationSource configurationSource) { 146 this.loggerContext = new WeakReference<>(loggerContext); 147 // The loggerContext is null for the NullConfiguration class. 148 // this.loggerContext = new WeakReference(Objects.requireNonNull(loggerContext, "loggerContext is null")); 149 this.configurationSource = Objects.requireNonNull(configurationSource, "configurationSource is null"); 150 componentMap.put(Configuration.CONTEXT_PROPERTIES, propertyMap); 151 pluginManager = new PluginManager(Node.CATEGORY); 152 rootNode = new Node(); 153 setState(State.INITIALIZING); 154 155 } 156 157 @Override 158 public ConfigurationSource getConfigurationSource() { 159 return configurationSource; 160 } 161 162 @Override 163 public List<String> getPluginPackages() { 164 return pluginPackages; 165 } 166 167 @Override 168 public Map<String, String> getProperties() { 169 return propertyMap; 170 } 171 172 @Override 173 public ScriptManager getScriptManager() { 174 return scriptManager; 175 } 176 177 public void setScriptManager(final ScriptManager scriptManager) { 178 this.scriptManager = scriptManager; 179 } 180 181 public PluginManager getPluginManager() { 182 return pluginManager; 183 } 184 185 public void setPluginManager(final PluginManager pluginManager) { 186 this.pluginManager = pluginManager; 187 } 188 189 @Override 190 public WatchManager getWatchManager() { 191 return watchManager; 192 } 193 194 @Override 195 public ConfigurationScheduler getScheduler() { 196 return configurationScheduler; 197 } 198 199 public Node getRootNode() { 200 return rootNode; 201 } 202 203 @Override 204 public AsyncLoggerConfigDelegate getAsyncLoggerConfigDelegate() { 205 // lazily instantiate only when requested by AsyncLoggers: 206 // loading AsyncLoggerConfigDisruptor requires LMAX Disruptor jar on classpath 207 if (asyncLoggerConfigDisruptor == null) { 208 asyncLoggerConfigDisruptor = new AsyncLoggerConfigDisruptor(); 209 } 210 return asyncLoggerConfigDisruptor; 211 } 212 213 /** 214 * Initialize the configuration. 215 */ 216 @Override 217 public void initialize() { 218 LOGGER.debug(Version.getProductString() + " initializing configuration {}", this); 219 subst.setConfiguration(this); 220 try { 221 scriptManager = new ScriptManager(this, watchManager); 222 } catch (final LinkageError | Exception e) { 223 // LOG4J2-1920 ScriptEngineManager is not available in Android 224 LOGGER.info("Cannot initialize scripting support because this JRE does not support it.", e); 225 } 226 pluginManager.collectPlugins(pluginPackages); 227 final PluginManager levelPlugins = new PluginManager(Level.CATEGORY); 228 levelPlugins.collectPlugins(pluginPackages); 229 final Map<String, PluginType<?>> plugins = levelPlugins.getPlugins(); 230 if (plugins != null) { 231 for (final PluginType<?> type : plugins.values()) { 232 try { 233 // Cause the class to be initialized if it isn't already. 234 Loader.initializeClass(type.getPluginClass().getName(), type.getPluginClass().getClassLoader()); 235 } catch (final Exception e) { 236 LOGGER.error("Unable to initialize {} due to {}", type.getPluginClass().getName(), e.getClass() 237 .getSimpleName(), e); 238 } 239 } 240 } 241 setup(); 242 setupAdvertisement(); 243 doConfigure(); 244 setState(State.INITIALIZED); 245 LOGGER.debug("Configuration {} initialized", this); 246 } 247 248 protected void initializeWatchers(Reconfigurable reconfigurable, ConfigurationSource configSource, 249 int monitorIntervalSeconds) { 250 if (configSource.getFile() != null || configSource.getURL() != null) { 251 if (monitorIntervalSeconds > 0) { 252 watchManager.setIntervalSeconds(monitorIntervalSeconds); 253 if (configSource.getFile() != null) { 254 final Source cfgSource = new Source(configSource); 255 final long lastModifeid = configSource.getFile().lastModified(); 256 final ConfigurationFileWatcher watcher = new ConfigurationFileWatcher(this, reconfigurable, 257 listeners, lastModifeid); 258 watchManager.watch(cfgSource, watcher); 259 } else if (configSource.getURL() != null) { 260 monitorSource(reconfigurable, configSource); 261 } 262 } else if (watchManager.hasEventListeners() && configSource.getURL() != null 263 && monitorIntervalSeconds >= 0) { 264 monitorSource(reconfigurable, configSource); 265 } 266 } 267 } 268 269 private void monitorSource(Reconfigurable reconfigurable, ConfigurationSource configSource) { 270 if (configSource.getLastModified() > 0) { 271 final Source cfgSource = new Source(configSource); 272 final Watcher watcher = WatcherFactory.getInstance(pluginPackages) 273 .newWatcher(cfgSource, this, reconfigurable, listeners, configSource.getLastModified()); 274 if (watcher != null) { 275 watchManager.watch(cfgSource, watcher); 276 } 277 } else { 278 LOGGER.info("{} does not support dynamic reconfiguration", configSource.getURI()); 279 } 280 } 281 282 /** 283 * Start the configuration. 284 */ 285 @Override 286 public void start() { 287 // Preserve the prior behavior of initializing during start if not initialized. 288 if (getState().equals(State.INITIALIZING)) { 289 initialize(); 290 } 291 LOGGER.debug("Starting configuration {}", this); 292 this.setStarting(); 293 if (watchManager.getIntervalSeconds() >= 0) { 294 watchManager.start(); 295 } 296 if (hasAsyncLoggers()) { 297 asyncLoggerConfigDisruptor.start(); 298 } 299 final Set<LoggerConfig> alreadyStarted = new HashSet<>(); 300 for (final LoggerConfig logger : loggerConfigs.values()) { 301 logger.start(); 302 alreadyStarted.add(logger); 303 } 304 for (final Appender appender : appenders.values()) { 305 appender.start(); 306 } 307 if (!alreadyStarted.contains(root)) { // LOG4J2-392 308 root.start(); // LOG4J2-336 309 } 310 super.start(); 311 LOGGER.debug("Started configuration {} OK.", this); 312 } 313 314 private boolean hasAsyncLoggers() { 315 if (root instanceof AsyncLoggerConfig) { 316 return true; 317 } 318 for (final LoggerConfig logger : loggerConfigs.values()) { 319 if (logger instanceof AsyncLoggerConfig) { 320 return true; 321 } 322 } 323 return false; 324 } 325 326 /** 327 * Tear down the configuration. 328 */ 329 @Override 330 public boolean stop(final long timeout, final TimeUnit timeUnit) { 331 this.setStopping(); 332 super.stop(timeout, timeUnit, false); 333 LOGGER.trace("Stopping {}...", this); 334 335 // Stop the components that are closest to the application first: 336 // 1. Notify all LoggerConfigs' ReliabilityStrategy that the configuration will be stopped. 337 // 2. Stop the LoggerConfig objects (this may stop nested Filters) 338 // 3. Stop the AsyncLoggerConfigDelegate. This shuts down the AsyncLoggerConfig Disruptor 339 // and waits until all events in the RingBuffer have been processed. 340 // 4. Stop all AsyncAppenders. This shuts down the associated thread and 341 // waits until all events in the queue have been processed. (With optional timeout.) 342 // 5. Notify all LoggerConfigs' ReliabilityStrategy that appenders will be stopped. 343 // This guarantees that any event received by a LoggerConfig before reconfiguration 344 // are passed on to the Appenders before the Appenders are stopped. 345 // 6. Stop the remaining running Appenders. (It should now be safe to do so.) 346 // 7. Notify all LoggerConfigs that their Appenders can be cleaned up. 347 348 for (final LoggerConfig loggerConfig : loggerConfigs.values()) { 349 loggerConfig.getReliabilityStrategy().beforeStopConfiguration(this); 350 } 351 root.getReliabilityStrategy().beforeStopConfiguration(this); 352 353 final String cls = getClass().getSimpleName(); 354 LOGGER.trace("{} notified {} ReliabilityStrategies that config will be stopped.", cls, loggerConfigs.size() 355 + 1); 356 357 if (!loggerConfigs.isEmpty()) { 358 LOGGER.trace("{} stopping {} LoggerConfigs.", cls, loggerConfigs.size()); 359 for (final LoggerConfig logger : loggerConfigs.values()) { 360 logger.stop(timeout, timeUnit); 361 } 362 } 363 LOGGER.trace("{} stopping root LoggerConfig.", cls); 364 if (!root.isStopped()) { 365 root.stop(timeout, timeUnit); 366 } 367 368 if (hasAsyncLoggers()) { 369 LOGGER.trace("{} stopping AsyncLoggerConfigDisruptor.", cls); 370 asyncLoggerConfigDisruptor.stop(timeout, timeUnit); 371 } 372 373 LOGGER.trace("{} notifying ReliabilityStrategies that appenders will be stopped.", cls); 374 for (final LoggerConfig loggerConfig : loggerConfigs.values()) { 375 loggerConfig.getReliabilityStrategy().beforeStopAppenders(); 376 } 377 root.getReliabilityStrategy().beforeStopAppenders(); 378 379 // Stop the appenders in reverse order in case they still have activity. 380 final Appender[] array = appenders.values().toArray(new Appender[appenders.size()]); 381 final List<Appender> async = getAsyncAppenders(array); 382 if (!async.isEmpty()) { 383 // LOG4J2-511, LOG4J2-392 stop AsyncAppenders first 384 LOGGER.trace("{} stopping {} AsyncAppenders.", cls, async.size()); 385 for (final Appender appender : async) { 386 if (appender instanceof LifeCycle2) { 387 ((LifeCycle2) appender).stop(timeout, timeUnit); 388 } else { 389 appender.stop(); 390 } 391 } 392 } 393 394 LOGGER.trace("{} stopping remaining Appenders.", cls); 395 int appenderCount = 0; 396 for (int i = array.length - 1; i >= 0; --i) { 397 if (array[i].isStarted()) { // then stop remaining Appenders 398 if (array[i] instanceof LifeCycle2) { 399 ((LifeCycle2) array[i]).stop(timeout, timeUnit); 400 } else { 401 array[i].stop(); 402 } 403 appenderCount++; 404 } 405 } 406 LOGGER.trace("{} stopped {} remaining Appenders.", cls, appenderCount); 407 408 LOGGER.trace("{} cleaning Appenders from {} LoggerConfigs.", cls, loggerConfigs.size() + 1); 409 for (final LoggerConfig loggerConfig : loggerConfigs.values()) { 410 411 // LOG4J2-520, LOG4J2-392: 412 // Important: do not clear appenders until after all AsyncLoggerConfigs 413 // have been stopped! Stopping the last AsyncLoggerConfig will 414 // shut down the disruptor and wait for all enqueued events to be processed. 415 // Only *after this* the appenders can be cleared or events will be lost. 416 loggerConfig.clearAppenders(); 417 } 418 root.clearAppenders(); 419 420 if (watchManager.isStarted()) { 421 watchManager.stop(timeout, timeUnit); 422 } 423 configurationScheduler.stop(timeout, timeUnit); 424 425 if (advertiser != null && advertisement != null) { 426 advertiser.unadvertise(advertisement); 427 } 428 setStopped(); 429 LOGGER.debug("Stopped {} OK", this); 430 return true; 431 } 432 433 private List<Appender> getAsyncAppenders(final Appender[] all) { 434 final List<Appender> result = new ArrayList<>(); 435 for (int i = all.length - 1; i >= 0; --i) { 436 if (all[i] instanceof AsyncAppender) { 437 result.add(all[i]); 438 } 439 } 440 return result; 441 } 442 443 @Override 444 public boolean isShutdownHookEnabled() { 445 return isShutdownHookEnabled; 446 } 447 448 @Override 449 public long getShutdownTimeoutMillis() { 450 return shutdownTimeoutMillis; 451 } 452 453 public void setup() { 454 // default does nothing, subclasses do work. 455 } 456 457 protected Level getDefaultStatus() { 458 final String statusLevel = PropertiesUtil.getProperties().getStringProperty( 459 Constants.LOG4J_DEFAULT_STATUS_LEVEL, Level.ERROR.name()); 460 try { 461 return Level.toLevel(statusLevel); 462 } catch (final Exception ex) { 463 return Level.ERROR; 464 } 465 } 466 467 protected void createAdvertiser(final String advertiserString, final ConfigurationSource configSource, 468 final byte[] buffer, final String contentType) { 469 if (advertiserString != null) { 470 final Node node = new Node(null, advertiserString, null); 471 final Map<String, String> attributes = node.getAttributes(); 472 attributes.put("content", new String(buffer)); 473 attributes.put("contentType", contentType); 474 attributes.put("name", "configuration"); 475 if (configSource.getLocation() != null) { 476 attributes.put("location", configSource.getLocation()); 477 } 478 advertiserNode = node; 479 } 480 } 481 482 private void setupAdvertisement() { 483 if (advertiserNode != null) { 484 final String nodeName = advertiserNode.getName(); 485 final PluginType<?> type = pluginManager.getPluginType(nodeName); 486 if (type != null) { 487 final Class<? extends Advertiser> clazz = type.getPluginClass().asSubclass(Advertiser.class); 488 try { 489 advertiser = clazz.newInstance(); 490 advertisement = advertiser.advertise(advertiserNode.getAttributes()); 491 } catch (final InstantiationException e) { 492 LOGGER.error("InstantiationException attempting to instantiate advertiser: {}", nodeName, e); 493 } catch (final IllegalAccessException e) { 494 LOGGER.error("IllegalAccessException attempting to instantiate advertiser: {}", nodeName, e); 495 } 496 } 497 } 498 } 499 500 @SuppressWarnings("unchecked") 501 @Override 502 public <T> T getComponent(final String componentName) { 503 return (T) componentMap.get(componentName); 504 } 505 506 @Override 507 public void addComponent(final String componentName, final Object obj) { 508 componentMap.putIfAbsent(componentName, obj); 509 } 510 511 protected void preConfigure(final Node node) { 512 try { 513 for (final Node child : node.getChildren()) { 514 if (child.getType() == null) { 515 LOGGER.error("Unable to locate plugin type for " + child.getName()); 516 continue; 517 } 518 final Class<?> clazz = child.getType().getPluginClass(); 519 if (clazz.isAnnotationPresent(Scheduled.class)) { 520 configurationScheduler.incrementScheduledItems(); 521 } 522 preConfigure(child); 523 } 524 } catch (final Exception ex) { 525 LOGGER.error("Error capturing node data for node " + node.getName(), ex); 526 } 527 } 528 529 /** 530 * Process conditions by evaluating them and including the children of conditions that are true 531 * and discarding those that are not. 532 * @param node The node to evaluate. 533 */ 534 protected void processConditionals(final Node node) { 535 try { 536 List<Node> addList = new ArrayList<>(); 537 List<Node> removeList = new ArrayList<>(); 538 for (final Node child : node.getChildren()) { 539 final PluginType<?> type = child.getType(); 540 if (type != null && Arbiter.ELEMENT_TYPE.equals(type.getElementName())) { 541 final Class<?> clazz = type.getPluginClass(); 542 if (SelectArbiter.class.isAssignableFrom(clazz)) { 543 removeList.add(child); 544 addList.addAll(processSelect(child, type)); 545 } else if (Arbiter.class.isAssignableFrom(clazz)) { 546 removeList.add(child); 547 try { 548 Arbiter condition = (Arbiter) createPluginObject(type, child, null); 549 if (condition.isCondition()) { 550 addList.addAll(child.getChildren()); 551 processConditionals(child); 552 } 553 } catch (final Exception inner) { 554 LOGGER.error("Exception processing {}: Ignoring and including children", 555 type.getPluginClass()); 556 processConditionals(child); 557 } 558 } else { 559 LOGGER.error("Encountered Condition Plugin that does not implement Condition: {}", 560 child.getName()); 561 processConditionals(child); 562 } 563 } else { 564 processConditionals(child); 565 } 566 } 567 if (!removeList.isEmpty()) { 568 List<Node> children = node.getChildren(); 569 children.removeAll(removeList); 570 children.addAll(addList); 571 for (Node grandChild : addList) { 572 grandChild.setParent(node); 573 } 574 } 575 } catch (final Exception ex) { 576 LOGGER.error("Error capturing node data for node " + node.getName(), ex); 577 } 578 } 579 580 /** 581 * Handle Select nodes. This finds the first child condition that returns true and attaches its children 582 * to the parent of the Select Node. Other Nodes are discarded. 583 * @param selectNode The Select Node. 584 * @param type The PluginType of the Select Node. 585 * @return The list of Nodes to be added to the parent. 586 */ 587 protected List<Node> processSelect(Node selectNode, PluginType<?> type) { 588 List<Node> addList = new ArrayList<>(); 589 SelectArbiter select = (SelectArbiter) createPluginObject(type, selectNode, null); 590 List<Arbiter> conditions = new ArrayList<>(); 591 for (Node child : selectNode.getChildren()) { 592 PluginType<?> nodeType = child.getType(); 593 if (nodeType != null) { 594 if (Arbiter.class.isAssignableFrom(nodeType.getPluginClass())) { 595 Arbiter condition = (Arbiter) createPluginObject(nodeType, child, null); 596 conditions.add(condition); 597 child.setObject(condition); 598 } else { 599 LOGGER.error("Invalid Node {} for Select. Must be a Condition", 600 child.getName()); 601 } 602 } else { 603 LOGGER.error("No PluginType for node {}", child.getName()); 604 } 605 } 606 Arbiter condition = select.evaluateConditions(conditions); 607 if (condition != null) { 608 for (Node child : selectNode.getChildren()) { 609 if (condition == child.getObject()) { 610 addList.addAll(child.getChildren()); 611 processConditionals(child); 612 } 613 } 614 } 615 return addList; 616 } 617 618 protected void doConfigure() { 619 processConditionals(rootNode); 620 preConfigure(rootNode); 621 configurationScheduler.start(); 622 if (rootNode.hasChildren() && rootNode.getChildren().get(0).getName().equalsIgnoreCase("Properties")) { 623 final Node first = rootNode.getChildren().get(0); 624 createConfiguration(first, null); 625 if (first.getObject() != null) { 626 subst.setVariableResolver((StrLookup) first.getObject()); 627 } 628 } else { 629 final Map<String, String> map = this.getComponent(CONTEXT_PROPERTIES); 630 final StrLookup lookup = map == null ? null : new MapLookup(map); 631 subst.setVariableResolver(new Interpolator(lookup, pluginPackages)); 632 } 633 634 boolean setLoggers = false; 635 boolean setRoot = false; 636 for (final Node child : rootNode.getChildren()) { 637 if (child.getName().equalsIgnoreCase("Properties")) { 638 if (tempLookup == subst.getVariableResolver()) { 639 LOGGER.error("Properties declaration must be the first element in the configuration"); 640 } 641 continue; 642 } 643 createConfiguration(child, null); 644 if (child.getObject() == null) { 645 continue; 646 } 647 if (child.getName().equalsIgnoreCase("Scripts")) { 648 for (final AbstractScript script : child.getObject(AbstractScript[].class)) { 649 if (script instanceof ScriptRef) { 650 LOGGER.error("Script reference to {} not added. Scripts definition cannot contain script references", 651 script.getName()); 652 } else if (scriptManager != null) { 653 scriptManager.addScript(script); 654 } 655 } 656 } else if (child.getName().equalsIgnoreCase("Appenders")) { 657 appenders = child.getObject(); 658 } else if (child.isInstanceOf(Filter.class)) { 659 addFilter(child.getObject(Filter.class)); 660 } else if (child.getName().equalsIgnoreCase("Loggers")) { 661 final Loggers l = child.getObject(); 662 loggerConfigs = l.getMap(); 663 setLoggers = true; 664 if (l.getRoot() != null) { 665 root = l.getRoot(); 666 setRoot = true; 667 } 668 } else if (child.getName().equalsIgnoreCase("CustomLevels")) { 669 customLevels = child.getObject(CustomLevels.class).getCustomLevels(); 670 } else if (child.isInstanceOf(CustomLevelConfig.class)) { 671 final List<CustomLevelConfig> copy = new ArrayList<>(customLevels); 672 copy.add(child.getObject(CustomLevelConfig.class)); 673 customLevels = copy; 674 } else { 675 final List<String> expected = Arrays.asList("\"Appenders\"", "\"Loggers\"", "\"Properties\"", 676 "\"Scripts\"", "\"CustomLevels\""); 677 LOGGER.error("Unknown object \"{}\" of type {} is ignored: try nesting it inside one of: {}.", 678 child.getName(), child.getObject().getClass().getName(), expected); 679 } 680 } 681 682 if (!setLoggers) { 683 LOGGER.warn("No Loggers were configured, using default. Is the Loggers element missing?"); 684 setToDefault(); 685 return; 686 } else if (!setRoot) { 687 LOGGER.warn("No Root logger was configured, creating default ERROR-level Root logger with Console appender"); 688 setToDefault(); 689 // return; // LOG4J2-219: creating default root=ok, but don't exclude configured Loggers 690 } 691 692 for (final Map.Entry<String, LoggerConfig> entry : loggerConfigs.entrySet()) { 693 final LoggerConfig loggerConfig = entry.getValue(); 694 for (final AppenderRef ref : loggerConfig.getAppenderRefs()) { 695 final Appender app = appenders.get(ref.getRef()); 696 if (app != null) { 697 loggerConfig.addAppender(app, ref.getLevel(), ref.getFilter()); 698 } else { 699 LOGGER.error("Unable to locate appender \"{}\" for logger config \"{}\"", ref.getRef(), 700 loggerConfig); 701 } 702 } 703 704 } 705 706 setParents(); 707 } 708 709 protected void setToDefault() { 710 // LOG4J2-1176 facilitate memory leak investigation 711 setName(DefaultConfiguration.DEFAULT_NAME + "@" + Integer.toHexString(hashCode())); 712 final Layout<? extends Serializable> layout = PatternLayout.newBuilder() 713 .withPattern(DefaultConfiguration.DEFAULT_PATTERN) 714 .withConfiguration(this) 715 .build(); 716 final Appender appender = ConsoleAppender.createDefaultAppenderForLayout(layout); 717 appender.start(); 718 addAppender(appender); 719 final LoggerConfig rootLoggerConfig = getRootLogger(); 720 rootLoggerConfig.addAppender(appender, null, null); 721 722 final Level defaultLevel = Level.ERROR; 723 final String levelName = PropertiesUtil.getProperties().getStringProperty(DefaultConfiguration.DEFAULT_LEVEL, 724 defaultLevel.name()); 725 final Level level = Level.valueOf(levelName); 726 rootLoggerConfig.setLevel(level != null ? level : defaultLevel); 727 } 728 729 /** 730 * Set the name of the configuration. 731 * 732 * @param name The name. 733 */ 734 public void setName(final String name) { 735 this.name = name; 736 } 737 738 /** 739 * Returns the name of the configuration. 740 * 741 * @return the name of the configuration. 742 */ 743 @Override 744 public String getName() { 745 return name; 746 } 747 748 /** 749 * Add a listener for changes on the configuration. 750 * 751 * @param listener The ConfigurationListener to add. 752 */ 753 @Override 754 public void addListener(final ConfigurationListener listener) { 755 listeners.add(listener); 756 } 757 758 /** 759 * Remove a ConfigurationListener. 760 * 761 * @param listener The ConfigurationListener to remove. 762 */ 763 @Override 764 public void removeListener(final ConfigurationListener listener) { 765 listeners.remove(listener); 766 } 767 768 /** 769 * Returns the Appender with the specified name. 770 * 771 * @param appenderName The name of the Appender. 772 * @return the Appender with the specified name or null if the Appender cannot be located. 773 */ 774 @Override 775 @SuppressWarnings("unchecked") 776 public <T extends Appender> T getAppender(final String appenderName) { 777 return appenderName != null ? (T) appenders.get(appenderName) : null; 778 } 779 780 /** 781 * Returns a Map containing all the Appenders and their name. 782 * 783 * @return A Map containing each Appender's name and the Appender object. 784 */ 785 @Override 786 public Map<String, Appender> getAppenders() { 787 return appenders; 788 } 789 790 /** 791 * Adds an Appender to the configuration. 792 * 793 * @param appender The Appender to add. 794 */ 795 @Override 796 public void addAppender(final Appender appender) { 797 if (appender != null) { 798 appenders.putIfAbsent(appender.getName(), appender); 799 } 800 } 801 802 @Override 803 public StrSubstitutor getStrSubstitutor() { 804 return subst; 805 } 806 807 @Override 808 public void setAdvertiser(final Advertiser advertiser) { 809 this.advertiser = advertiser; 810 } 811 812 @Override 813 public Advertiser getAdvertiser() { 814 return advertiser; 815 } 816 817 /* 818 * (non-Javadoc) 819 * 820 * @see org.apache.logging.log4j.core.config.ReliabilityStrategyFactory#getReliabilityStrategy(org.apache.logging.log4j 821 * .core.config.LoggerConfig) 822 */ 823 @Override 824 public ReliabilityStrategy getReliabilityStrategy(final LoggerConfig loggerConfig) { 825 return ReliabilityStrategyFactory.getReliabilityStrategy(loggerConfig); 826 } 827 828 /** 829 * Associates an Appender with a LoggerConfig. This method is synchronized in case a Logger with the same name is 830 * being updated at the same time. 831 * 832 * Note: This method is not used when configuring via configuration. It is primarily used by unit tests. 833 * 834 * @param logger The Logger the Appender will be associated with. 835 * @param appender The Appender. 836 */ 837 @Override 838 public synchronized void addLoggerAppender(final org.apache.logging.log4j.core.Logger logger, 839 final Appender appender) { 840 if (appender == null || logger == null) { 841 return; 842 } 843 final String loggerName = logger.getName(); 844 appenders.putIfAbsent(appender.getName(), appender); 845 final LoggerConfig lc = getLoggerConfig(loggerName); 846 if (lc.getName().equals(loggerName)) { 847 lc.addAppender(appender, null, null); 848 } else { 849 final LoggerConfig nlc = new LoggerConfig(loggerName, lc.getLevel(), lc.isAdditive()); 850 nlc.addAppender(appender, null, null); 851 nlc.setParent(lc); 852 loggerConfigs.putIfAbsent(loggerName, nlc); 853 setParents(); 854 logger.getContext().updateLoggers(); 855 } 856 } 857 858 /** 859 * Associates a Filter with a LoggerConfig. This method is synchronized in case a Logger with the same name is being 860 * updated at the same time. 861 * 862 * Note: This method is not used when configuring via configuration. It is primarily used by unit tests. 863 * 864 * @param logger The Logger the Footer will be associated with. 865 * @param filter The Filter. 866 */ 867 @Override 868 public synchronized void addLoggerFilter(final org.apache.logging.log4j.core.Logger logger, final Filter filter) { 869 final String loggerName = logger.getName(); 870 final LoggerConfig lc = getLoggerConfig(loggerName); 871 if (lc.getName().equals(loggerName)) { 872 lc.addFilter(filter); 873 } else { 874 final LoggerConfig nlc = new LoggerConfig(loggerName, lc.getLevel(), lc.isAdditive()); 875 nlc.addFilter(filter); 876 nlc.setParent(lc); 877 loggerConfigs.putIfAbsent(loggerName, nlc); 878 setParents(); 879 logger.getContext().updateLoggers(); 880 } 881 } 882 883 /** 884 * Marks a LoggerConfig as additive. This method is synchronized in case a Logger with the same name is being 885 * updated at the same time. 886 * 887 * Note: This method is not used when configuring via configuration. It is primarily used by unit tests. 888 * 889 * @param logger The Logger the Appender will be associated with. 890 * @param additive True if the LoggerConfig should be additive, false otherwise. 891 */ 892 @Override 893 public synchronized void setLoggerAdditive(final org.apache.logging.log4j.core.Logger logger, final boolean additive) { 894 final String loggerName = logger.getName(); 895 final LoggerConfig lc = getLoggerConfig(loggerName); 896 if (lc.getName().equals(loggerName)) { 897 lc.setAdditive(additive); 898 } else { 899 final LoggerConfig nlc = new LoggerConfig(loggerName, lc.getLevel(), additive); 900 nlc.setParent(lc); 901 loggerConfigs.putIfAbsent(loggerName, nlc); 902 setParents(); 903 logger.getContext().updateLoggers(); 904 } 905 } 906 907 /** 908 * Remove an Appender. First removes any associations between LoggerConfigs and the Appender, removes the Appender 909 * from this appender list and then stops the appender. This method is synchronized in case an Appender with the 910 * same name is being added during the removal. 911 * 912 * @param appenderName the name of the appender to remove. 913 */ 914 public synchronized void removeAppender(final String appenderName) { 915 for (final LoggerConfig logger : loggerConfigs.values()) { 916 logger.removeAppender(appenderName); 917 } 918 final Appender app = appenderName != null ? appenders.remove(appenderName) : null; 919 920 if (app != null) { 921 app.stop(); 922 } 923 } 924 925 /* 926 * (non-Javadoc) 927 * 928 * @see org.apache.logging.log4j.core.config.Configuration#getCustomLevels() 929 */ 930 @Override 931 public List<CustomLevelConfig> getCustomLevels() { 932 return Collections.unmodifiableList(customLevels); 933 } 934 935 /** 936 * Locates the appropriate LoggerConfig for a Logger name. This will remove tokens from the package name as 937 * necessary or return the root LoggerConfig if no other matches were found. 938 * 939 * @param loggerName The Logger name. 940 * @return The located LoggerConfig. 941 */ 942 @Override 943 public LoggerConfig getLoggerConfig(final String loggerName) { 944 LoggerConfig loggerConfig = loggerConfigs.get(loggerName); 945 if (loggerConfig != null) { 946 return loggerConfig; 947 } 948 String substr = loggerName; 949 while ((substr = NameUtil.getSubName(substr)) != null) { 950 loggerConfig = loggerConfigs.get(substr); 951 if (loggerConfig != null) { 952 return loggerConfig; 953 } 954 } 955 return root; 956 } 957 958 @Override 959 public LoggerContext getLoggerContext() { 960 return loggerContext.get(); 961 } 962 963 /** 964 * Returns the root Logger. 965 * 966 * @return the root Logger. 967 */ 968 @Override 969 public LoggerConfig getRootLogger() { 970 return root; 971 } 972 973 /** 974 * Returns a Map of all the LoggerConfigs. 975 * 976 * @return a Map with each entry containing the name of the Logger and the LoggerConfig. 977 */ 978 @Override 979 public Map<String, LoggerConfig> getLoggers() { 980 return Collections.unmodifiableMap(loggerConfigs); 981 } 982 983 /** 984 * Returns the LoggerConfig with the specified name. 985 * 986 * @param loggerName The Logger name. 987 * @return The LoggerConfig or null if no match was found. 988 */ 989 public LoggerConfig getLogger(final String loggerName) { 990 return loggerConfigs.get(loggerName); 991 } 992 993 /** 994 * Add a loggerConfig. The LoggerConfig must already be configured with Appenders, Filters, etc. After addLogger is 995 * called LoggerContext.updateLoggers must be called. 996 * 997 * @param loggerName The name of the Logger. 998 * @param loggerConfig The LoggerConfig. 999 */ 1000 @Override 1001 public synchronized void addLogger(final String loggerName, final LoggerConfig loggerConfig) { 1002 loggerConfigs.putIfAbsent(loggerName, loggerConfig); 1003 setParents(); 1004 } 1005 1006 /** 1007 * Remove a LoggerConfig. 1008 * 1009 * @param loggerName The name of the Logger. 1010 */ 1011 @Override 1012 public synchronized void removeLogger(final String loggerName) { 1013 loggerConfigs.remove(loggerName); 1014 setParents(); 1015 } 1016 1017 @Override 1018 public void createConfiguration(final Node node, final LogEvent event) { 1019 final PluginType<?> type = node.getType(); 1020 if (type != null && type.isDeferChildren()) { 1021 node.setObject(createPluginObject(type, node, event)); 1022 } else { 1023 for (final Node child : node.getChildren()) { 1024 createConfiguration(child, event); 1025 } 1026 1027 if (type == null) { 1028 if (node.getParent() != null) { 1029 LOGGER.error("Unable to locate plugin for {}", node.getName()); 1030 } 1031 } else { 1032 node.setObject(createPluginObject(type, node, event)); 1033 } 1034 } 1035 } 1036 1037 /** 1038 * This method is used by Arbiters to create specific children. 1039 * @param type The PluginType. 1040 * @param node The Node. 1041 * @return The created object or null; 1042 */ 1043 public Object createPluginObject(final PluginType<?> type, final Node node) { 1044 if (this.getState().equals(State.INITIALIZING)) { 1045 return createPluginObject(type, node, null); 1046 } else { 1047 LOGGER.warn("Plugin Object creation is not allowed after initialization"); 1048 return null; 1049 } 1050 } 1051 1052 /** 1053 * Invokes a static factory method to either create the desired object or to create a builder object that creates 1054 * the desired object. In the case of a factory method, it should be annotated with 1055 * {@link org.apache.logging.log4j.core.config.plugins.PluginFactory}, and each parameter should be annotated with 1056 * an appropriate plugin annotation depending on what that parameter describes. Parameters annotated with 1057 * {@link org.apache.logging.log4j.core.config.plugins.PluginAttribute} must be a type that can be converted from a 1058 * string using one of the {@link org.apache.logging.log4j.core.config.plugins.convert.TypeConverter TypeConverters} 1059 * . Parameters with {@link org.apache.logging.log4j.core.config.plugins.PluginElement} may be any plugin class or 1060 * an array of a plugin class. Collections and Maps are currently not supported, although the factory method that is 1061 * called can create these from an array. 1062 * 1063 * Plugins can also be created using a builder class that implements 1064 * {@link org.apache.logging.log4j.core.util.Builder}. In that case, a static method annotated with 1065 * {@link org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute} should create the builder class, and 1066 * the various fields in the builder class should be annotated similarly to the method parameters. However, instead 1067 * of using PluginAttribute, one should use 1068 * {@link org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute} where the default value can be 1069 * specified as the default field value instead of as an additional annotation parameter. 1070 * 1071 * In either case, there are also annotations for specifying a 1072 * {@link org.apache.logging.log4j.core.config.Configuration} ( 1073 * {@link org.apache.logging.log4j.core.config.plugins.PluginConfiguration}) or a 1074 * {@link org.apache.logging.log4j.core.config.Node} ( 1075 * {@link org.apache.logging.log4j.core.config.plugins.PluginNode}). 1076 * 1077 * Although the happy path works, more work still needs to be done to log incorrect parameters. These will generally 1078 * result in unhelpful InvocationTargetExceptions. 1079 * 1080 * @param type the type of plugin to create. 1081 * @param node the corresponding configuration node for this plugin to create. 1082 * @param event the LogEvent that spurred the creation of this plugin 1083 * @return the created plugin object or {@code null} if there was an error setting it up. 1084 * @see org.apache.logging.log4j.core.config.plugins.util.PluginBuilder 1085 * @see org.apache.logging.log4j.core.config.plugins.visitors.PluginVisitor 1086 * @see org.apache.logging.log4j.core.config.plugins.convert.TypeConverter 1087 */ 1088 private Object createPluginObject(final PluginType<?> type, final Node node, final LogEvent event) { 1089 final Class<?> clazz = type.getPluginClass(); 1090 1091 if (Map.class.isAssignableFrom(clazz)) { 1092 try { 1093 return createPluginMap(node); 1094 } catch (final Exception e) { 1095 LOGGER.warn("Unable to create Map for {} of class {}", type.getElementName(), clazz, e); 1096 } 1097 } 1098 1099 if (Collection.class.isAssignableFrom(clazz)) { 1100 try { 1101 return createPluginCollection(node); 1102 } catch (final Exception e) { 1103 LOGGER.warn("Unable to create List for {} of class {}", type.getElementName(), clazz, e); 1104 } 1105 } 1106 1107 return new PluginBuilder(type).withConfiguration(this).withConfigurationNode(node).forLogEvent(event).build(); 1108 } 1109 1110 private static Map<String, ?> createPluginMap(final Node node) { 1111 final Map<String, Object> map = new LinkedHashMap<>(); 1112 for (final Node child : node.getChildren()) { 1113 final Object object = child.getObject(); 1114 map.put(child.getName(), object); 1115 } 1116 return map; 1117 } 1118 1119 private static Collection<?> createPluginCollection(final Node node) { 1120 final List<Node> children = node.getChildren(); 1121 final Collection<Object> list = new ArrayList<>(children.size()); 1122 for (final Node child : children) { 1123 final Object object = child.getObject(); 1124 list.add(object); 1125 } 1126 return list; 1127 } 1128 1129 private void setParents() { 1130 for (final Map.Entry<String, LoggerConfig> entry : loggerConfigs.entrySet()) { 1131 final LoggerConfig logger = entry.getValue(); 1132 String key = entry.getKey(); 1133 if (!key.isEmpty()) { 1134 final int i = key.lastIndexOf('.'); 1135 if (i > 0) { 1136 key = key.substring(0, i); 1137 LoggerConfig parent = getLoggerConfig(key); 1138 if (parent == null) { 1139 parent = root; 1140 } 1141 logger.setParent(parent); 1142 } else { 1143 logger.setParent(root); 1144 } 1145 } 1146 } 1147 } 1148 1149 /** 1150 * Reads an InputStream using buffered reads into a byte array buffer. The given InputStream will remain open after 1151 * invocation of this method. 1152 * 1153 * @param is the InputStream to read into a byte array buffer. 1154 * @return a byte array of the InputStream contents. 1155 * @throws IOException if the {@code read} method of the provided InputStream throws this exception. 1156 */ 1157 protected static byte[] toByteArray(final InputStream is) throws IOException { 1158 final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 1159 1160 int nRead; 1161 final byte[] data = new byte[BUF_SIZE]; 1162 1163 while ((nRead = is.read(data, 0, data.length)) != -1) { 1164 buffer.write(data, 0, nRead); 1165 } 1166 1167 return buffer.toByteArray(); 1168 } 1169 1170 @Override 1171 public NanoClock getNanoClock() { 1172 return nanoClock; 1173 } 1174 1175 @Override 1176 public void setNanoClock(final NanoClock nanoClock) { 1177 this.nanoClock = Objects.requireNonNull(nanoClock, "nanoClock"); 1178 } 1179}