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