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    }