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 org.apache.logging.log4j.core.config.Configuration;
20  import org.apache.logging.log4j.core.config.ConfigurationFactory;
21  import org.apache.logging.log4j.core.config.ConfigurationListener;
22  import org.apache.logging.log4j.core.config.DefaultConfiguration;
23  import org.apache.logging.log4j.core.config.NullConfiguration;
24  import org.apache.logging.log4j.core.config.Reconfigurable;
25  import org.apache.logging.log4j.status.StatusLogger;
26  
27  import java.io.File;
28  import java.net.URI;
29  import java.util.concurrent.ConcurrentHashMap;
30  import java.util.concurrent.ConcurrentMap;
31  import java.util.concurrent.locks.Lock;
32  import java.util.concurrent.locks.ReentrantLock;
33  
34  /**
35   * The LoggerContext is the anchor for the logging system. It maintains a list of all the loggers requested by
36   * applications and a reference to the Configuration. The Configuration will contain the configured loggers, appenders,
37   * filters, etc and will be atomically updated whenever a reconfigure occurs.
38   */
39  public class LoggerContext implements org.apache.logging.log4j.spi.LoggerContext, ConfigurationListener, LifeCycle {
40  
41      private static final StatusLogger logger = StatusLogger.getLogger();
42  
43      private final ConcurrentMap<String, Logger> loggers = new ConcurrentHashMap<String, Logger>();
44  
45      /**
46       * The Configuration is volatile to guarantee that initialization of the Configuration has completed before
47       * the reference is updated.
48       */
49      private volatile Configuration config = new DefaultConfiguration();
50  
51      private Object externalContext;
52  
53      private final String name;
54  
55      private final URI configLocation;
56  
57      /**
58       * Status of the LoggerContext.
59       */
60      public enum Status {
61          /** Initialized but not yet started. */
62          INITIALIZED,
63          /** In the process of starting. */
64          STARTING,
65          /** Is active. */
66          STARTED,
67          /** Shutdown is in progress. */
68          STOPPING,
69          /** Has shutdown. */
70          STOPPED
71      }
72  
73      private volatile Status status = Status.INITIALIZED;
74  
75      private final Lock configLock = new ReentrantLock();
76  
77      /**
78       * Constructor taking only a name.
79       * @param name The context name.
80       */
81      public LoggerContext(String name) {
82          this(name, null, (URI) null);
83      }
84  
85      /**
86       * Constructor taking a name and a reference to an external context.
87       * @param name The context name.
88       * @param externalContext The external context.
89       */
90      public LoggerContext(String name, Object externalContext) {
91          this(name, externalContext, (URI) null);
92      }
93  
94      /**
95       * Constructor taking a name, external context and a configuration URI.
96       * @param name The context name.
97       * @param externalContext The external context.
98       * @param configLocn The location of the configuration as a URI.
99       */
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 }