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