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