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        } else {
130            return initialize(name, loader, NetUtils.toURI(configLocation), externalContext);
131        }
132    }
133
134    /**
135     * Initializes the Logging Context.
136     * @param name The Context name.
137     * @param loader The ClassLoader for the Context (or null).
138     * @param configLocation The configuration for the logging context.
139     * @return The LoggerContext.
140     */
141    public static LoggerContext initialize(final String name, final ClassLoader loader, final URI configLocation) {
142        return initialize(name, loader, configLocation, null);
143    }
144
145    /**
146     * Initializes the Logging Context.
147     * @param name The Context name.
148     * @param loader The ClassLoader for the Context (or null).
149     * @param configLocation The configuration for the logging context (or null).
150     * @param externalContext The external context to be attached to the LoggerContext
151     * @return The LoggerContext.
152     */
153    public static LoggerContext initialize(final String name, final ClassLoader loader, final URI configLocation,
154                                           final Object externalContext) {
155
156        try {
157            final Log4jContextFactory factory = getFactory();
158            return factory == null ? null :
159                    factory.getContext(FQCN, loader, externalContext, false, configLocation, name);
160        } catch (final Exception ex) {
161            LOGGER.error("There was a problem initializing the LoggerContext [{}] using configuration at [{}].",
162                    name, configLocation, ex);
163        }
164        return null;
165    }
166
167    public static LoggerContext initialize(final String name, final ClassLoader loader, final List<URI> configLocations,
168            final Object externalContext) {
169        try {
170            final Log4jContextFactory factory = getFactory();
171            return factory == null ?
172                    null :
173                    factory.getContext(FQCN, loader, externalContext, false, configLocations, name);
174        } catch (final Exception ex) {
175            LOGGER.error("There was a problem initializing the LoggerContext [{}] using configurations at [{}].", name,
176                    configLocations, ex);
177        }
178        return null;
179    }
180
181    /**
182     * Initializes the Logging Context.
183     * @param name The Context name.
184     * @param configLocation The configuration for the logging context.
185     * @return The LoggerContext or null if an error occurred (check the status logger).
186     */
187    public static LoggerContext initialize(final String name, final String configLocation) {
188        return initialize(name, null, configLocation);
189    }
190
191    /**
192     * Initializes the Logging Context.
193     * @param configuration The Configuration.
194     * @return The LoggerContext.
195     */
196    public static LoggerContext initialize(final Configuration configuration) {
197        return initialize(null, configuration, null);
198    }
199
200    /**
201     * Initializes the Logging Context.
202     * @param loader The ClassLoader.
203     * @param configuration The Configuration.
204     * @return The LoggerContext.
205     */
206    public static LoggerContext initialize(final ClassLoader loader, final Configuration configuration) {
207        return initialize(loader, configuration, null);
208    }
209
210    /**
211     * Initializes the Logging Context.
212     * @param loader The ClassLoader.
213     * @param configuration The Configuration.
214     * @param externalContext - The external context to be attached to the LoggerContext.
215     * @return The LoggerContext.
216     */
217    public static LoggerContext initialize(final ClassLoader loader, final Configuration configuration, final Object externalContext) {
218        try {
219            final Log4jContextFactory factory = getFactory();
220            return factory == null ? null :
221                    factory.getContext(FQCN, loader, externalContext, false, configuration);
222        } catch (final Exception ex) {
223            LOGGER.error("There was a problem initializing the LoggerContext using configuration {}",
224                    configuration.getName(), ex);
225        }
226        return null;
227    }
228
229    /**
230     * Sets the levels of <code>parentLogger</code> and all 'child' loggers to the given <code>level</code>.
231     * @param parentLogger the parent logger
232     * @param level the new level
233     */
234    public static void setAllLevels(final String parentLogger, final Level level) {
235        // 1) get logger config
236        // 2) if exact match, use it, if not, create it.
237        // 3) set level on logger config
238        // 4) update child logger configs with level
239        // 5) update loggers
240        final LoggerContext loggerContext = LoggerContext.getContext(false);
241        final Configuration config = loggerContext.getConfiguration();
242        boolean set = setLevel(parentLogger, level, config);
243        for (final Map.Entry<String, LoggerConfig> entry : config.getLoggers().entrySet()) {
244            if (entry.getKey().startsWith(parentLogger)) {
245                set |= setLevel(entry.getValue(), level);
246            }
247        }
248        if (set) {
249            loggerContext.updateLoggers();
250        }
251    }
252
253    private static boolean setLevel(final LoggerConfig loggerConfig, final Level level) {
254        final boolean set = !loggerConfig.getLevel().equals(level);
255        if (set) {
256            loggerConfig.setLevel(level);
257        }
258        return set;
259    }
260
261    /**
262     * Sets logger levels.
263     *
264     * @param levelMap
265     *            a levelMap where keys are level names and values are new
266     *            Levels.
267     */
268    public static void setLevel(final Map<String, Level> levelMap) {
269        final LoggerContext loggerContext = LoggerContext.getContext(false);
270        final Configuration config = loggerContext.getConfiguration();
271        boolean set = false;
272        for (final Map.Entry<String, Level> entry : levelMap.entrySet()) {
273            final String loggerName = entry.getKey();
274            final Level level = entry.getValue();
275            set |= setLevel(loggerName, level, config);
276        }
277        if (set) {
278            loggerContext.updateLoggers();
279        }
280    }
281
282    /**
283     * Sets a logger's level.
284     *
285     * @param loggerName
286     *            the logger name
287     * @param level
288     *            the new level
289     */
290    public static void setLevel(final String loggerName, final Level level) {
291        final LoggerContext loggerContext = LoggerContext.getContext(false);
292        if (Strings.isEmpty(loggerName)) {
293            setRootLevel(level);
294        } else {
295            if (setLevel(loggerName, level, loggerContext.getConfiguration())) {
296                loggerContext.updateLoggers();
297            }
298        }
299    }
300
301    private static boolean setLevel(final String loggerName, final Level level, final Configuration config) {
302        boolean set;
303        LoggerConfig loggerConfig = config.getLoggerConfig(loggerName);
304        if (!loggerName.equals(loggerConfig.getName())) {
305            // TODO Should additivity be inherited?
306            loggerConfig = new LoggerConfig(loggerName, level, true);
307            config.addLogger(loggerName, loggerConfig);
308            loggerConfig.setLevel(level);
309            set = true;
310        } else {
311            set = setLevel(loggerConfig, level);
312        }
313        return set;
314    }
315
316    /**
317     * Sets the root logger's level.
318     *
319     * @param level
320     *            the new level
321     */
322    public static void setRootLevel(final Level level) {
323        final LoggerContext loggerContext = LoggerContext.getContext(false);
324        final LoggerConfig loggerConfig = loggerContext.getConfiguration().getRootLogger();
325        if (!loggerConfig.getLevel().equals(level)) {
326            loggerConfig.setLevel(level);
327            loggerContext.updateLoggers();
328        }
329    }
330
331    /**
332     * Shuts down the given logging context.
333     * @param ctx the logging context to shut down, may be null.
334     */
335    public static void shutdown(final LoggerContext ctx) {
336        if (ctx != null) {
337            ctx.stop();
338        }
339    }
340
341    private Configurator() {
342        // empty
343    }
344}