001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 package org.apache.logging.log4j.core; 018 019 import java.io.File; 020 import java.net.URI; 021 import java.util.HashMap; 022 import java.util.Map; 023 import java.util.concurrent.ConcurrentHashMap; 024 import java.util.concurrent.ConcurrentMap; 025 import java.util.concurrent.locks.Lock; 026 import java.util.concurrent.locks.ReentrantLock; 027 028 import org.apache.logging.log4j.core.config.Configuration; 029 import org.apache.logging.log4j.core.config.ConfigurationFactory; 030 import org.apache.logging.log4j.core.config.ConfigurationListener; 031 import org.apache.logging.log4j.core.config.DefaultConfiguration; 032 import org.apache.logging.log4j.core.config.NullConfiguration; 033 import org.apache.logging.log4j.core.config.Reconfigurable; 034 import org.apache.logging.log4j.core.helpers.NetUtils; 035 import org.apache.logging.log4j.message.MessageFactory; 036 import org.apache.logging.log4j.spi.AbstractLogger; 037 import org.apache.logging.log4j.status.StatusLogger; 038 039 /** 040 * The LoggerContext is the anchor for the logging system. It maintains a list of all the loggers requested by 041 * applications and a reference to the Configuration. The Configuration will contain the configured loggers, appenders, 042 * filters, etc and will be atomically updated whenever a reconfigure occurs. 043 */ 044 public class LoggerContext implements org.apache.logging.log4j.spi.LoggerContext, ConfigurationListener, LifeCycle { 045 046 private static final StatusLogger LOGGER = StatusLogger.getLogger(); 047 048 private final ConcurrentMap<String, Logger> loggers = new ConcurrentHashMap<String, Logger>(); 049 050 /** 051 * The Configuration is volatile to guarantee that initialization of the Configuration has completed before 052 * the reference is updated. 053 */ 054 private volatile Configuration config = new DefaultConfiguration(); 055 056 private Object externalContext; 057 058 private final String name; 059 060 private final URI configLocation; 061 062 /** 063 * Status of the LoggerContext. 064 */ 065 public enum Status { 066 /** Initialized but not yet started. */ 067 INITIALIZED, 068 /** In the process of starting. */ 069 STARTING, 070 /** Is active. */ 071 STARTED, 072 /** Shutdown is in progress. */ 073 STOPPING, 074 /** Has shutdown. */ 075 STOPPED 076 } 077 078 private volatile Status status = Status.INITIALIZED; 079 080 private final Lock configLock = new ReentrantLock(); 081 082 /** 083 * Constructor taking only a name. 084 * @param name The context name. 085 */ 086 public LoggerContext(final String name) { 087 this(name, null, (URI) null); 088 } 089 090 /** 091 * Constructor taking a name and a reference to an external context. 092 * @param name The context name. 093 * @param externalContext The external context. 094 */ 095 public LoggerContext(final String name, final Object externalContext) { 096 this(name, externalContext, (URI) null); 097 } 098 099 /** 100 * Constructor taking a name, external context and a configuration URI. 101 * @param name The context name. 102 * @param externalContext The external context. 103 * @param configLocn The location of the configuration as a URI. 104 */ 105 public LoggerContext(final String name, final Object externalContext, final URI configLocn) { 106 this.name = name; 107 this.externalContext = externalContext; 108 this.configLocation = configLocn; 109 } 110 111 /** 112 * Constructor taking a name external context and a configuration location String. The location 113 * must be resolvable to a File. 114 * @param name The configuration location. 115 * @param externalContext The external context. 116 * @param configLocn The configuration location. 117 */ 118 public LoggerContext(final String name, final Object externalContext, final String configLocn) { 119 this.name = name; 120 this.externalContext = externalContext; 121 if (configLocn != null) { 122 URI uri; 123 try { 124 uri = new File(configLocn).toURI(); 125 } catch (final Exception ex) { 126 uri = null; 127 } 128 configLocation = uri; 129 } else { 130 configLocation = null; 131 } 132 } 133 134 public void start() { 135 if (configLock.tryLock()) { 136 try { 137 if (status == Status.INITIALIZED) { 138 status = Status.STARTING; 139 reconfigure(); 140 status = Status.STARTED; 141 } 142 } finally { 143 configLock.unlock(); 144 } 145 } 146 } 147 148 public void stop() { 149 configLock.lock(); 150 try { 151 status = Status.STOPPING; 152 updateLoggers(new NullConfiguration()); 153 config.stop(); 154 externalContext = null; 155 status = Status.STOPPED; 156 } finally { 157 configLock.unlock(); 158 } 159 } 160 161 /** 162 * Gets the name. 163 * 164 * @return the name. 165 */ 166 public String getName() { 167 return name; 168 } 169 170 public Status getStatus() { 171 return status; 172 } 173 174 public boolean isStarted() { 175 return status == Status.STARTED; 176 } 177 178 /** 179 * Set the external context. 180 * @param context The external context. 181 */ 182 public void setExternalContext(final Object context) { 183 this.externalContext = context; 184 } 185 186 /** 187 * Returns the external context. 188 * @return The external context. 189 */ 190 public Object getExternalContext() { 191 return this.externalContext; 192 } 193 194 /** 195 * Obtain a Logger from the Context. 196 * @param name The name of the Logger to return. 197 * @return The Logger. 198 */ 199 public Logger getLogger(final String name) { 200 return getLogger(name, null); 201 } 202 203 /** 204 * Obtain a Logger from the Context. 205 * @param name The name of the Logger to return. 206 * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change 207 * the logger but will log a warning if mismatched. 208 * @return The Logger. 209 */ 210 public Logger getLogger(final String name, final MessageFactory messageFactory) { 211 Logger logger = loggers.get(name); 212 if (logger != null) { 213 AbstractLogger.checkMessageFactory(logger, messageFactory); 214 return logger; 215 } 216 217 logger = newInstance(this, name, messageFactory); 218 final Logger prev = loggers.putIfAbsent(name, logger); 219 return prev == null ? logger : prev; 220 } 221 222 /** 223 * Determine if the specified Logger exists. 224 * @param name The Logger name to search for. 225 * @return True if the Logger exists, false otherwise. 226 */ 227 public boolean hasLogger(final String name) { 228 return loggers.containsKey(name); 229 } 230 231 /** 232 * Returns the current Configuration. The Configuration will be replaced when a reconfigure occurs. 233 * @return The Configuration. 234 */ 235 public Configuration getConfiguration() { 236 return config; 237 } 238 239 /** 240 * Add a Filter to the Configuration. Filters that are added through the API will be lost 241 * when a reconfigure occurs. 242 * @param filter The Filter to add. 243 */ 244 public void addFilter(final Filter filter) { 245 config.addFilter(filter); 246 } 247 248 /** 249 * Removes a Filter from the current Configuration. 250 * @param filter The Filter to remove. 251 */ 252 public void removeFiler(final Filter filter) { 253 config.removeFilter(filter); 254 } 255 256 /** 257 * Set the Configuration to be used. 258 * @param config The new Configuration. 259 * @return The previous Configuration. 260 */ 261 public synchronized Configuration setConfiguration(final Configuration config) { 262 if (config == null) { 263 throw new NullPointerException("No Configuration was provided"); 264 } 265 final Configuration prev = this.config; 266 config.addListener(this); 267 final Map<String, String> map = new HashMap<String, String>(); 268 map.put("hostName", NetUtils.getLocalHostname()); 269 map.put("contextName", name); 270 config.addComponent(Configuration.CONTEXT_PROPERTIES, map); 271 config.start(); 272 this.config = config; 273 updateLoggers(); 274 if (prev != null) { 275 prev.removeListener(this); 276 prev.stop(); 277 } 278 return prev; 279 } 280 281 /** 282 * Reconfigure the context. 283 */ 284 public synchronized void reconfigure() { 285 LOGGER.debug("Reconfiguration started for context " + name); 286 final Configuration instance = ConfigurationFactory.getInstance().getConfiguration(name, configLocation); 287 setConfiguration(instance); 288 /*instance.start(); 289 Configuration old = setConfiguration(instance); 290 updateLoggers(); 291 if (old != null) { 292 old.stop(); 293 } */ 294 LOGGER.debug("Reconfiguration completed"); 295 } 296 297 /** 298 * Cause all Loggers to be updated against the current Configuration. 299 */ 300 public void updateLoggers() { 301 updateLoggers(this.config); 302 } 303 304 /** 305 * Cause all Logger to be updated against the specified Configuration. 306 * @param config The Configuration. 307 */ 308 public void updateLoggers(final Configuration config) { 309 for (final Logger logger : loggers.values()) { 310 logger.updateConfiguration(config); 311 } 312 } 313 314 /** 315 * Cause a reconfiguration to take place when the underlying configuration file changes. 316 * @param reconfigurable The Configuration that can be reconfigured. 317 */ 318 public synchronized void onChange(final Reconfigurable reconfigurable) { 319 LOGGER.debug("Reconfiguration started for context " + name); 320 final Configuration config = reconfigurable.reconfigure(); 321 if (config != null) { 322 setConfiguration(config); 323 LOGGER.debug("Reconfiguration completed"); 324 } else { 325 LOGGER.debug("Reconfiguration failed"); 326 } 327 } 328 329 330 private Logger newInstance(final LoggerContext ctx, final String name, final MessageFactory messageFactory) { 331 return new Logger(ctx, name, messageFactory); 332 } 333 334 }