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 */
017package org.apache.logging.log4j.core.config;
018
019import java.net.URI;
020import java.util.Map;
021
022import org.apache.logging.log4j.Level;
023import org.apache.logging.log4j.LogManager;
024import org.apache.logging.log4j.Logger;
025import org.apache.logging.log4j.core.LoggerContext;
026import org.apache.logging.log4j.core.impl.Log4jContextFactory;
027import org.apache.logging.log4j.core.util.NetUtils;
028import org.apache.logging.log4j.spi.LoggerContextFactory;
029import org.apache.logging.log4j.status.StatusLogger;
030import org.apache.logging.log4j.util.Strings;
031
032/**
033 * Initializes and configure the Logging system. This class provides several ways to construct a LoggerContext using
034 * the location of a configuration file, a context name, and various optional parameters.
035 */
036public final class Configurator {
037
038    private static final String FQCN = Configurator.class.getName();
039
040    private static final Logger LOGGER = StatusLogger.getLogger();
041
042    private static Log4jContextFactory getFactory() {
043        final LoggerContextFactory factory = LogManager.getFactory();
044        if (factory instanceof Log4jContextFactory) {
045            return (Log4jContextFactory) factory;
046        } else if (factory != null) {
047            LOGGER.error("LogManager returned an instance of {} which does not implement {}. Unable to initialize Log4j.",
048                    factory.getClass().getName(), Log4jContextFactory.class.getName());
049            return null;
050        } else {
051            LOGGER.fatal("LogManager did not return a LoggerContextFactory. This indicates something has gone terribly wrong!");
052            return null;
053        }
054    }
055
056    /**
057     * Initializes the Logging Context.
058     * @param loader The ClassLoader for the Context (or null).
059     * @param source The InputSource for the configuration.
060     * @return The LoggerContext.
061     */
062    public static LoggerContext initialize(final ClassLoader loader,
063                                           final ConfigurationSource source) {
064        return initialize(loader, source, null);
065    }
066
067    /**
068     * Initializes the Logging Context.
069     * @param loader The ClassLoader for the Context (or null).
070     * @param source The InputSource for the configuration.
071     * @param externalContext The external context to be attached to the LoggerContext.
072     * @return The LoggerContext.
073     */
074
075    public static LoggerContext initialize(final ClassLoader loader,
076                                           final ConfigurationSource source,
077                                           final Object externalContext)
078    {
079
080        try {
081            final Log4jContextFactory factory = getFactory();
082            return factory == null ? null :
083                    factory.getContext(FQCN, loader, externalContext, false, source);
084        } catch (final Exception ex) {
085            LOGGER.error("There was a problem obtaining a LoggerContext using the configuration source [{}]", source, ex);
086        }
087        return null;
088    }
089
090    /**
091     * Initializes the Logging Context.
092     * @param name The Context name.
093     * @param loader The ClassLoader for the Context (or null).
094     * @param configLocation The configuration for the logging context.
095     * @return The LoggerContext or null if an error occurred (check the status logger).
096     */
097    public static LoggerContext initialize(final String name, final ClassLoader loader, final String configLocation) {
098        return initialize(name, loader, configLocation, null);
099
100    }
101
102    /**
103     * Initializes the Logging Context.
104     * @param name The Context name.
105     * @param loader The ClassLoader for the Context (or null).
106     * @param configLocation The configuration for the logging context (or null, or blank).
107     * @param externalContext The external context to be attached to the LoggerContext
108     * @return The LoggerContext or null if an error occurred (check the status logger).
109     */
110    public static LoggerContext initialize(final String name, final ClassLoader loader, final String configLocation,
111            final Object externalContext) {
112        final URI uri = Strings.isBlank(configLocation) ? null : NetUtils.toURI(configLocation);
113        return initialize(name, loader, uri, externalContext);
114    }
115
116    /**
117     * Initializes the Logging Context.
118     * @param name The Context name.
119     * @param loader The ClassLoader for the Context (or null).
120     * @param configLocation The configuration for the logging context.
121     * @return The LoggerContext.
122     */
123    public static LoggerContext initialize(final String name, final ClassLoader loader, final URI configLocation) {
124        return initialize(name, loader, configLocation, null);
125    }
126
127    /**
128     * Initializes the Logging Context.
129     * @param name The Context name.
130     * @param loader The ClassLoader for the Context (or null).
131     * @param configLocation The configuration for the logging context (or null).
132     * @param externalContext The external context to be attached to the LoggerContext
133     * @return The LoggerContext.
134     */
135    public static LoggerContext initialize(final String name, final ClassLoader loader, final URI configLocation,
136                                           final Object externalContext) {
137
138        try {
139            final Log4jContextFactory factory = getFactory();
140            return factory == null ? null :
141                    factory.getContext(FQCN, loader, externalContext, false, configLocation, name);
142        } catch (final Exception ex) {
143            LOGGER.error("There was a problem initializing the LoggerContext [{}] using configuration at [{}].",
144                    name, configLocation, ex);
145        }
146        return null;
147    }
148
149    /**
150     * Initializes the Logging Context.
151     * @param name The Context name.
152     * @param configLocation The configuration for the logging context.
153     * @return The LoggerContext or null if an error occurred (check the status logger).
154     */
155    public static LoggerContext initialize(final String name, final String configLocation) {
156        return initialize(name, null, configLocation);
157    }
158
159    /**
160     * Initializes the Logging Context.
161     * @param configuration The Configuration.
162     * @return The LoggerContext.
163     */
164    public static LoggerContext initialize(Configuration configuration) {
165        return initialize(null, configuration, null);
166    }
167
168    /**
169     * Initializes the Logging Context.
170     * @param loader The ClassLoader.
171     * @param configuration The Configuration.
172     * @return The LoggerContext.
173     */
174    public static LoggerContext initialize(final ClassLoader loader, Configuration configuration) {
175        return initialize(loader, configuration, null);
176    }
177
178    /**
179     * Initializes the Logging Context.
180     * @param loader The ClassLoader.
181     * @param configuration The Configuration.
182     * @param externalContext - The external context to be attached to the LoggerContext.
183     * @return The LoggerContext.
184     */
185    public static LoggerContext initialize(final ClassLoader loader, Configuration configuration, final Object externalContext) {
186        try {
187            final Log4jContextFactory factory = getFactory();
188            return factory == null ? null :
189                    factory.getContext(FQCN, loader, externalContext, false, configuration);
190        } catch (final Exception ex) {
191            LOGGER.error("There was a problem initializing the LoggerContext using configuration {}",
192                    configuration.getName(), ex);
193        }
194        return null;
195    }
196
197    /**
198     * Sets the levels of <code>parentLogger</code> and all 'child' loggers to the given <code>level</code>.
199     * @param parentLogger the parent logger
200     * @param level the new level
201     */
202    public static void setAllLevels(final String parentLogger, final Level level) {
203        // 1) get logger config
204        // 2) if exact match, use it, if not, create it.
205        // 3) set level on logger config 
206        // 4) update child logger configs with level
207        // 5) update loggers
208        final LoggerContext loggerContext = LoggerContext.getContext(false);
209        final Configuration config = loggerContext.getConfiguration();
210        boolean set = setLevel(parentLogger, level, config);
211        for (final Map.Entry<String, LoggerConfig> entry : config.getLoggers().entrySet()) {
212            if (entry.getKey().startsWith(parentLogger)) {
213                set |= setLevel(entry.getValue(), level);
214            }
215        }
216        if (set) {
217            loggerContext.updateLoggers();
218        }
219    }
220
221    private static boolean setLevel(final LoggerConfig loggerConfig, final Level level) {
222        final boolean set = !loggerConfig.getLevel().equals(level);
223        if (set) {
224            loggerConfig.setLevel(level);
225        }
226        return set;
227    }
228    
229    /**
230     * Sets logger levels.
231     * 
232     * @param levelMap
233     *            a levelMap where keys are level names and values are new
234     *            Levels.
235     */
236    public static void setLevel(final Map<String, Level> levelMap) {
237        final LoggerContext loggerContext = LoggerContext.getContext(false);
238        final Configuration config = loggerContext.getConfiguration();
239        boolean set = false;
240        for (final Map.Entry<String, Level> entry : levelMap.entrySet()) {
241            final String loggerName = entry.getKey();
242            final Level level = entry.getValue();
243            set |= setLevel(loggerName, level, config);
244        }
245        if (set) {
246            loggerContext.updateLoggers();
247        }
248    }
249
250    /**
251     * Sets a logger's level.
252     * 
253     * @param loggerName
254     *            the logger name
255     * @param level
256     *            the new level
257     */
258    public static void setLevel(final String loggerName, final Level level) {
259        final LoggerContext loggerContext = LoggerContext.getContext(false);
260        if (Strings.isEmpty(loggerName)) {
261            setRootLevel(level);
262        } else {
263            if (setLevel(loggerName, level, loggerContext.getConfiguration())) {
264                loggerContext.updateLoggers();
265            }
266        }
267    }
268
269    private static boolean setLevel(final String loggerName, final Level level, final Configuration config) {
270        boolean set;
271        LoggerConfig loggerConfig = config.getLoggerConfig(loggerName);
272        if (!loggerName.equals(loggerConfig.getName())) {
273            // TODO Should additivity be inherited?
274            loggerConfig = new LoggerConfig(loggerName, level, true);
275            config.addLogger(loggerName, loggerConfig);
276            loggerConfig.setLevel(level);
277            set = true;
278        } else {
279            set = setLevel(loggerConfig, level);
280        }
281        return set;
282    }
283
284    /**
285     * Sets the root logger's level.
286     * 
287     * @param level
288     *            the new level
289     */
290    public static void setRootLevel(final Level level) {
291        final LoggerContext loggerContext = LoggerContext.getContext(false);
292        final LoggerConfig loggerConfig = loggerContext.getConfiguration().getRootLogger();
293        if (!loggerConfig.getLevel().equals(level)) {
294            loggerConfig.setLevel(level);
295            loggerContext.updateLoggers();
296        }
297    }
298
299    /**
300     * Shuts down the given logging context.
301     * @param ctx the logging context to shut down, may be null.
302     */
303    public static void shutdown(final LoggerContext ctx) {
304        if (ctx != null) {
305            ctx.stop();
306        }
307    }
308
309    private Configurator() {
310        // empty
311    }
312}