View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core;
18  
19  import java.io.File;
20  import java.net.URI;
21  import java.util.HashMap;
22  import java.util.Map;
23  import java.util.concurrent.ConcurrentHashMap;
24  import java.util.concurrent.ConcurrentMap;
25  import java.util.concurrent.locks.Lock;
26  import java.util.concurrent.locks.ReentrantLock;
27  
28  import org.apache.logging.log4j.core.config.Configuration;
29  import org.apache.logging.log4j.core.config.ConfigurationFactory;
30  import org.apache.logging.log4j.core.config.ConfigurationListener;
31  import org.apache.logging.log4j.core.config.DefaultConfiguration;
32  import org.apache.logging.log4j.core.config.NullConfiguration;
33  import org.apache.logging.log4j.core.config.Reconfigurable;
34  import org.apache.logging.log4j.core.helpers.NetUtils;
35  import org.apache.logging.log4j.message.MessageFactory;
36  import org.apache.logging.log4j.spi.AbstractLogger;
37  import org.apache.logging.log4j.status.StatusLogger;
38  
39  /**
40   * The LoggerContext is the anchor for the logging system. It maintains a list of all the loggers requested by
41   * applications and a reference to the Configuration. The Configuration will contain the configured loggers, appenders,
42   * filters, etc and will be atomically updated whenever a reconfigure occurs.
43   */
44  public class LoggerContext implements org.apache.logging.log4j.spi.LoggerContext, ConfigurationListener, LifeCycle {
45  
46      private static final StatusLogger LOGGER = StatusLogger.getLogger();
47  
48      private final ConcurrentMap<String, Logger> loggers = new ConcurrentHashMap<String, Logger>();
49  
50      /**
51       * The Configuration is volatile to guarantee that initialization of the Configuration has completed before
52       * the reference is updated.
53       */
54      private volatile Configuration config = new DefaultConfiguration();
55  
56      private Object externalContext;
57  
58      private final String name;
59  
60      private final URI configLocation;
61  
62      /**
63       * Status of the LoggerContext.
64       */
65      public enum Status {
66          /** Initialized but not yet started. */
67          INITIALIZED,
68          /** In the process of starting. */
69          STARTING,
70          /** Is active. */
71          STARTED,
72          /** Shutdown is in progress. */
73          STOPPING,
74          /** Has shutdown. */
75          STOPPED
76      }
77  
78      private volatile Status status = Status.INITIALIZED;
79  
80      private final Lock configLock = new ReentrantLock();
81  
82      /**
83       * Constructor taking only a name.
84       * @param name The context name.
85       */
86      public LoggerContext(final String name) {
87          this(name, null, (URI) null);
88      }
89  
90      /**
91       * Constructor taking a name and a reference to an external context.
92       * @param name The context name.
93       * @param externalContext The external context.
94       */
95      public LoggerContext(final String name, final Object externalContext) {
96          this(name, externalContext, (URI) null);
97      }
98  
99      /**
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 }