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.composite; 018 019import java.lang.reflect.InvocationTargetException; 020import java.net.URI; 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.List; 024import java.util.Map; 025 026import org.apache.logging.log4j.Level; 027import org.apache.logging.log4j.core.config.AbstractConfiguration; 028import org.apache.logging.log4j.core.config.Configuration; 029import org.apache.logging.log4j.core.config.ConfigurationFactory; 030import org.apache.logging.log4j.core.config.ConfigurationSource; 031import org.apache.logging.log4j.core.config.Node; 032import org.apache.logging.log4j.core.config.Reconfigurable; 033import org.apache.logging.log4j.core.config.plugins.util.ResolverUtil; 034import org.apache.logging.log4j.core.config.status.StatusConfiguration; 035import org.apache.logging.log4j.core.util.Loader; 036import org.apache.logging.log4j.core.util.Patterns; 037import org.apache.logging.log4j.core.util.Source; 038import org.apache.logging.log4j.core.util.WatchManager; 039import org.apache.logging.log4j.core.util.Watcher; 040import org.apache.logging.log4j.util.PropertiesUtil; 041 042/** 043 * A Composite Configuration. 044 */ 045public class CompositeConfiguration extends AbstractConfiguration implements Reconfigurable { 046 047 /** 048 * Allow the ConfigurationFactory class to be specified as a system property. 049 */ 050 public static final String MERGE_STRATEGY_PROPERTY = "log4j.mergeStrategy"; 051 052 private static final String[] VERBOSE_CLASSES = new String[] {ResolverUtil.class.getName()}; 053 054 private final List<? extends AbstractConfiguration> configurations; 055 056 private MergeStrategy mergeStrategy; 057 058 /** 059 * Construct the CompositeConfiguration. 060 * 061 * @param configurations The List of Configurations to merge. 062 */ 063 public CompositeConfiguration(final List<? extends AbstractConfiguration> configurations) { 064 super(configurations.get(0).getLoggerContext(), ConfigurationSource.COMPOSITE_SOURCE); 065 rootNode = configurations.get(0).getRootNode(); 066 this.configurations = configurations; 067 final String mergeStrategyClassName = PropertiesUtil.getProperties().getStringProperty(MERGE_STRATEGY_PROPERTY, 068 DefaultMergeStrategy.class.getName()); 069 try { 070 mergeStrategy = Loader.newInstanceOf(mergeStrategyClassName); 071 } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException | InvocationTargetException | 072 InstantiationException ex) { 073 mergeStrategy = new DefaultMergeStrategy(); 074 } 075 for (final AbstractConfiguration config : configurations) { 076 mergeStrategy.mergeRootProperties(rootNode, config); 077 } 078 final StatusConfiguration statusConfig = new StatusConfiguration().withVerboseClasses(VERBOSE_CLASSES) 079 .withStatus(getDefaultStatus()); 080 for (final Map.Entry<String, String> entry : rootNode.getAttributes().entrySet()) { 081 final String key = entry.getKey(); 082 final String value = getStrSubstitutor().replace(entry.getValue()); 083 if ("status".equalsIgnoreCase(key)) { 084 statusConfig.withStatus(value.toUpperCase()); 085 } else if ("dest".equalsIgnoreCase(key)) { 086 statusConfig.withDestination(value); 087 } else if ("shutdownHook".equalsIgnoreCase(key)) { 088 isShutdownHookEnabled = !"disable".equalsIgnoreCase(value); 089 } else if ("shutdownTimeout".equalsIgnoreCase(key)) { 090 shutdownTimeoutMillis = Long.parseLong(value); 091 } else if ("verbose".equalsIgnoreCase(key)) { 092 statusConfig.withVerbosity(value); 093 } else if ("packages".equalsIgnoreCase(key)) { 094 pluginPackages.addAll(Arrays.asList(value.split(Patterns.COMMA_SEPARATOR))); 095 } else if ("name".equalsIgnoreCase(key)) { 096 setName(value); 097 } 098 } 099 statusConfig.initialize(); 100 } 101 102 @Override 103 public void setup() { 104 final AbstractConfiguration targetConfiguration = configurations.get(0); 105 staffChildConfiguration(targetConfiguration); 106 final WatchManager watchManager = getWatchManager(); 107 final WatchManager targetWatchManager = targetConfiguration.getWatchManager(); 108 if (targetWatchManager.getIntervalSeconds() > 0) { 109 watchManager.setIntervalSeconds(targetWatchManager.getIntervalSeconds()); 110 final Map<Source, Watcher> watchers = targetWatchManager.getConfigurationWatchers(); 111 for (final Map.Entry<Source, Watcher> entry : watchers.entrySet()) { 112 watchManager.watch(entry.getKey(), entry.getValue().newWatcher(this, listeners, 113 entry.getValue().getLastModified())); 114 } 115 } 116 for (final AbstractConfiguration sourceConfiguration : configurations.subList(1, configurations.size())) { 117 staffChildConfiguration(sourceConfiguration); 118 final Node sourceRoot = sourceConfiguration.getRootNode(); 119 mergeStrategy.mergConfigurations(rootNode, sourceRoot, getPluginManager()); 120 if (LOGGER.isEnabled(Level.ALL)) { 121 final StringBuilder sb = new StringBuilder(); 122 printNodes("", rootNode, sb); 123 System.out.println(sb.toString()); 124 } 125 final int monitorInterval = sourceConfiguration.getWatchManager().getIntervalSeconds(); 126 if (monitorInterval > 0) { 127 final int currentInterval = watchManager.getIntervalSeconds(); 128 if (currentInterval <= 0 || monitorInterval < currentInterval) { 129 watchManager.setIntervalSeconds(monitorInterval); 130 } 131 final WatchManager sourceWatchManager = sourceConfiguration.getWatchManager(); 132 final Map<Source, Watcher> watchers = sourceWatchManager.getConfigurationWatchers(); 133 for (final Map.Entry<Source, Watcher> entry : watchers.entrySet()) { 134 watchManager.watch(entry.getKey(), entry.getValue().newWatcher(this, listeners, 135 entry.getValue().getLastModified())); 136 } 137 } 138 } 139 } 140 141 @Override 142 public Configuration reconfigure() { 143 LOGGER.debug("Reconfiguring composite configuration"); 144 final List<AbstractConfiguration> configs = new ArrayList<>(); 145 final ConfigurationFactory factory = ConfigurationFactory.getInstance(); 146 for (final AbstractConfiguration config : configurations) { 147 final ConfigurationSource source = config.getConfigurationSource(); 148 final URI sourceURI = source.getURI(); 149 Configuration currentConfig = config; 150 if (sourceURI == null) { 151 LOGGER.warn("Unable to determine URI for configuration {}, changes to it will be ignored", 152 config.getName()); 153 } else { 154 currentConfig = factory.getConfiguration(getLoggerContext(), config.getName(), sourceURI); 155 if (currentConfig == null) { 156 LOGGER.warn("Unable to reload configuration {}, changes to it will be ignored", config.getName()); 157 } 158 } 159 configs.add((AbstractConfiguration) currentConfig); 160 161 } 162 163 return new CompositeConfiguration(configs); 164 } 165 166 private void staffChildConfiguration(final AbstractConfiguration childConfiguration) { 167 childConfiguration.setPluginManager(pluginManager); 168 childConfiguration.setScriptManager(scriptManager); 169 childConfiguration.setup(); 170 } 171 172 private void printNodes(final String indent, final Node node, final StringBuilder sb) { 173 sb.append(indent).append(node.getName()).append(" type: ").append(node.getType()).append("\n"); 174 sb.append(indent).append(node.getAttributes().toString()).append("\n"); 175 for (final Node child : node.getChildren()) { 176 printNodes(indent + " ", child, sb); 177 } 178 } 179 180 @Override 181 public String toString() { 182 return getClass().getName() + "@" + Integer.toHexString(hashCode()) + " [configurations=" + configurations 183 + ", mergeStrategy=" + mergeStrategy + ", rootNode=" + rootNode + ", listeners=" + listeners 184 + ", pluginPackages=" + pluginPackages + ", pluginManager=" + pluginManager + ", isShutdownHookEnabled=" 185 + isShutdownHookEnabled + ", shutdownTimeoutMillis=" + shutdownTimeoutMillis + ", scriptManager=" 186 + scriptManager + "]"; 187 } 188}