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 org.apache.logging.log4j.core.config.Configuration; 020 import org.apache.logging.log4j.core.config.ConfigurationFactory; 021 import org.apache.logging.log4j.core.config.ConfigurationListener; 022 import org.apache.logging.log4j.core.config.DefaultConfiguration; 023 import org.apache.logging.log4j.core.config.NullConfiguration; 024 import org.apache.logging.log4j.core.config.Reconfigurable; 025 import org.apache.logging.log4j.status.StatusLogger; 026 027 import java.io.File; 028 import java.net.URI; 029 import java.util.concurrent.ConcurrentHashMap; 030 import java.util.concurrent.ConcurrentMap; 031 import java.util.concurrent.locks.Lock; 032 import java.util.concurrent.locks.ReentrantLock; 033 034 /** 035 * The LoggerContext is the anchor for the logging system. It maintains a list of all the loggers requested by 036 * applications and a reference to the Configuration. The Configuration will contain the configured loggers, appenders, 037 * filters, etc and will be atomically updated whenever a reconfigure occurs. 038 */ 039 public class LoggerContext implements org.apache.logging.log4j.spi.LoggerContext, ConfigurationListener, LifeCycle { 040 041 private static final StatusLogger logger = StatusLogger.getLogger(); 042 043 private final ConcurrentMap<String, Logger> loggers = new ConcurrentHashMap<String, Logger>(); 044 045 /** 046 * The Configuration is volatile to guarantee that initialization of the Configuration has completed before 047 * the reference is updated. 048 */ 049 private volatile Configuration config = new DefaultConfiguration(); 050 051 private Object externalContext; 052 053 private final String name; 054 055 private final URI configLocation; 056 057 /** 058 * Status of the LoggerContext. 059 */ 060 public enum Status { 061 /** Initialized but not yet started. */ 062 INITIALIZED, 063 /** In the process of starting. */ 064 STARTING, 065 /** Is active. */ 066 STARTED, 067 /** Shutdown is in progress. */ 068 STOPPING, 069 /** Has shutdown. */ 070 STOPPED 071 } 072 073 private volatile Status status = Status.INITIALIZED; 074 075 private final Lock configLock = new ReentrantLock(); 076 077 /** 078 * Constructor taking only a name. 079 * @param name The context name. 080 */ 081 public LoggerContext(String name) { 082 this(name, null, (URI) null); 083 } 084 085 /** 086 * Constructor taking a name and a reference to an external context. 087 * @param name The context name. 088 * @param externalContext The external context. 089 */ 090 public LoggerContext(String name, Object externalContext) { 091 this(name, externalContext, (URI) null); 092 } 093 094 /** 095 * Constructor taking a name, external context and a configuration URI. 096 * @param name The context name. 097 * @param externalContext The external context. 098 * @param configLocn The location of the configuration as a URI. 099 */ 100 public LoggerContext(String name, Object externalContext, URI configLocn) { 101 this.name = name; 102 this.externalContext = externalContext; 103 this.configLocation = configLocn; 104 } 105 106 /** 107 * Constructor taking a name external context and a configuration location String. The location 108 * must be resolvable to a File. 109 * @param name The configuration location. 110 * @param externalContext The external context. 111 * @param configLocn The configuration location. 112 */ 113 public LoggerContext(String name, Object externalContext, String configLocn) { 114 this.name = name; 115 this.externalContext = externalContext; 116 if (configLocn != null) { 117 URI uri; 118 try { 119 uri = new File(configLocn).toURI(); 120 } catch (Exception ex) { 121 uri = null; 122 } 123 configLocation = uri; 124 } else { 125 configLocation = null; 126 } 127 } 128 129 public void start() { 130 if (configLock.tryLock()) { 131 try { 132 if (status == Status.INITIALIZED) { 133 status = Status.STARTING; 134 reconfigure(); 135 status = Status.STARTED; 136 } 137 } finally { 138 configLock.unlock(); 139 } 140 } 141 } 142 143 public void stop() { 144 configLock.lock(); 145 try { 146 status = Status.STOPPING; 147 updateLoggers(new NullConfiguration()); 148 config.stop(); 149 externalContext = null; 150 status = Status.STOPPED; 151 } finally { 152 configLock.unlock(); 153 } 154 } 155 156 /** 157 * Gets the name. 158 * 159 * @return the name. 160 */ 161 public String getName() { 162 return name; 163 } 164 165 public Status getStatus() { 166 return status; 167 } 168 169 public boolean isStarted() { 170 return status == Status.STARTED; 171 } 172 173 /** 174 * Set the external context. 175 * @param context The external context. 176 */ 177 public void setExternalContext(Object context) { 178 this.externalContext = context; 179 } 180 181 /** 182 * Returns the external context. 183 * @return The external context. 184 */ 185 public Object getExternalContext() { 186 return this.externalContext; 187 } 188 189 /** 190 * Obtain a Logger from the Context. 191 * @param name The name of the Logger to return. 192 * @return The Logger. 193 */ 194 public Logger getLogger(String name) { 195 196 Logger logger = loggers.get(name); 197 if (logger != null) { 198 return logger; 199 } 200 201 logger = newInstance(this, name); 202 Logger prev = loggers.putIfAbsent(name, logger); 203 return prev == null ? logger : prev; 204 } 205 206 /** 207 * Determine if the specified Logger exists. 208 * @param name The Logger name to search for. 209 * @return True if the Logger exists, false otherwise. 210 */ 211 public boolean hasLogger(String name) { 212 return loggers.containsKey(name); 213 } 214 215 /** 216 * Returns the current Configuration. The Configuration will be replaced when a reconfigure occurs. 217 * @return The Configuration. 218 */ 219 public Configuration getConfiguration() { 220 return config; 221 } 222 223 /** 224 * Add a Filter to the Configuration. Filters that are added through the API will be lost 225 * when a reconfigure occurs. 226 * @param filter The Filter to add. 227 */ 228 public void addFilter(Filter filter) { 229 config.addFilter(filter); 230 } 231 232 /** 233 * Removes a Filter from the current Configuration. 234 * @param filter The Filter to remove. 235 */ 236 public void removeFiler(Filter filter) { 237 config.removeFilter(filter); 238 } 239 240 /** 241 * Set the Configuration to be used. 242 * @param config The new Configuration. 243 * @return The previous Configuration. 244 */ 245 public synchronized Configuration setConfiguration(Configuration config) { 246 if (config == null) { 247 throw new NullPointerException("No Configuration was provided"); 248 } 249 Configuration prev = this.config; 250 config.addListener(this); 251 config.start(); 252 this.config = config; 253 updateLoggers(); 254 if (prev != null) { 255 prev.removeListener(this); 256 prev.stop(); 257 } 258 return prev; 259 } 260 261 /** 262 * Reconfigure the context. 263 */ 264 public synchronized void reconfigure() { 265 logger.debug("Reconfiguration started for context " + name); 266 Configuration instance = ConfigurationFactory.getInstance().getConfiguration(name, configLocation); 267 setConfiguration(instance); 268 /*instance.start(); 269 Configuration old = setConfiguration(instance); 270 updateLoggers(); 271 if (old != null) { 272 old.stop(); 273 } */ 274 logger.debug("Reconfiguration completed"); 275 } 276 277 /** 278 * Cause all Loggers to be updated against the current Configuration. 279 */ 280 public void updateLoggers() { 281 updateLoggers(this.config); 282 } 283 284 /** 285 * Cause all Logger to be updated against the specified Configuration. 286 * @param config The Configuration. 287 */ 288 public void updateLoggers(Configuration config) { 289 for (Logger logger : loggers.values()) { 290 logger.updateConfiguration(config); 291 } 292 } 293 294 /** 295 * Cause a reconfiguration to take place when the underlying configuration file changes. 296 * @param reconfigurable The Configuration that can be reconfigured. 297 */ 298 public synchronized void onChange(Reconfigurable reconfigurable) { 299 logger.debug("Reconfiguration started for context " + name); 300 Configuration config = reconfigurable.reconfigure(); 301 if (config != null) { 302 setConfiguration(config); 303 logger.debug("Reconfiguration completed"); 304 } else { 305 logger.debug("Reconfiguration failed"); 306 } 307 } 308 309 310 private Logger newInstance(LoggerContext ctx, String name) { 311 return new Logger(ctx, name); 312 } 313 314 }