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; 023import java.util.concurrent.TimeUnit; 024 025import org.apache.logging.log4j.Level; 026import org.apache.logging.log4j.LogManager; 027import org.apache.logging.log4j.Logger; 028import org.apache.logging.log4j.core.LoggerContext; 029import org.apache.logging.log4j.core.impl.Log4jContextFactory; 030import org.apache.logging.log4j.core.util.NetUtils; 031import org.apache.logging.log4j.spi.LoggerContextFactory; 032import org.apache.logging.log4j.status.StatusLogger; 033import org.apache.logging.log4j.util.Strings; 034 035/** 036 * Initializes and configure the Logging system. This class provides several ways to construct a LoggerContext using 037 * the location of a configuration file, a context name, and various optional parameters. 038 */ 039public final class Configurator { 040 041 private static final String FQCN = Configurator.class.getName(); 042 043 private static final Logger LOGGER = StatusLogger.getLogger(); 044 045 private static Log4jContextFactory getFactory() { 046 final LoggerContextFactory factory = LogManager.getFactory(); 047 if (factory instanceof Log4jContextFactory) { 048 return (Log4jContextFactory) factory; 049 } else if (factory != null) { 050 LOGGER.error("LogManager returned an instance of {} which does not implement {}. Unable to initialize Log4j.", 051 factory.getClass().getName(), Log4jContextFactory.class.getName()); 052 return null; 053 } else { 054 LOGGER.fatal("LogManager did not return a LoggerContextFactory. This indicates something has gone terribly wrong!"); 055 return null; 056 } 057 } 058 059 /** 060 * Initializes the Logging Context. 061 * @param loader The ClassLoader for the Context (or null). 062 * @param source The InputSource for the configuration. 063 * @return The LoggerContext. 064 */ 065 public static LoggerContext initialize(final ClassLoader loader, 066 final ConfigurationSource source) { 067 return initialize(loader, source, null); 068 } 069 070 /** 071 * Initializes the Logging Context. 072 * @param loader The ClassLoader for the Context (or null). 073 * @param source The InputSource for the configuration. 074 * @param externalContext The external context to be attached to the LoggerContext. 075 * @return The LoggerContext. 076 */ 077 078 public static LoggerContext initialize(final ClassLoader loader, 079 final ConfigurationSource source, 080 final Object externalContext) 081 { 082 083 try { 084 final Log4jContextFactory factory = getFactory(); 085 return factory == null ? null : 086 factory.getContext(FQCN, loader, externalContext, false, source); 087 } catch (final Exception ex) { 088 LOGGER.error("There was a problem obtaining a LoggerContext using the configuration source [{}]", source, ex); 089 } 090 return null; 091 } 092 093 /** 094 * Initializes the Logging Context. 095 * @param name The Context name. 096 * @param loader The ClassLoader for the Context (or null). 097 * @param configLocation The configuration for the logging context. 098 * @return The LoggerContext or null if an error occurred (check the status logger). 099 */ 100 public static LoggerContext initialize(final String name, final ClassLoader loader, final String configLocation) { 101 return initialize(name, loader, configLocation, null); 102 103 } 104 105 /** 106 * Initializes the Logging Context. 107 * @param name The Context name. 108 * @param loader The ClassLoader for the Context (or null). 109 * @param configLocation The configuration for the logging context (or null, or blank). 110 * @param externalContext The external context to be attached to the LoggerContext 111 * @return The LoggerContext or null if an error occurred (check the status logger). 112 */ 113 public static LoggerContext initialize(final String name, final ClassLoader loader, final String configLocation, 114 final Object externalContext) { 115 if (Strings.isBlank(configLocation)) { 116 return initialize(name, loader, (URI) null, externalContext); 117 } 118 if (configLocation.contains(",")) { 119 final String[] parts = configLocation.split(","); 120 String scheme = null; 121 final List<URI> uris = new ArrayList<>(parts.length); 122 for (final String part : parts) { 123 final URI uri = NetUtils.toURI(scheme != null ? scheme + ":" + part.trim() : part.trim()); 124 if (scheme == null && uri.getScheme() != null) { 125 scheme = uri.getScheme(); 126 } 127 uris.add(uri); 128 } 129 return initialize(name, loader, uris, externalContext); 130 } 131 return initialize(name, loader, NetUtils.toURI(configLocation), externalContext); 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 * Reload the existing reconfiguration. 231 * @since 2.12.0 232 */ 233 public static void reconfigure() { 234 try { 235 Log4jContextFactory factory = getFactory(); 236 if (factory != null) { 237 factory.getSelector().getContext(FQCN, null, false).reconfigure(); 238 } else { 239 LOGGER.warn("Unable to reconfigure - Log4j has not been initialized."); 240 } 241 } catch (final Exception ex) { 242 LOGGER.error("Error encountered trying to reconfigure logging", ex); 243 } 244 } 245 246 /** 247 * Reconfigure with a potentially new configuration. 248 * @param uri The location of the configuration. 249 * @since 2.12.0 250 */ 251 public static void reconfigure(final URI uri) { 252 try { 253 Log4jContextFactory factory = getFactory(); 254 if (factory != null) { 255 factory.getSelector().getContext(FQCN, null, false).setConfigLocation(uri); 256 } else { 257 LOGGER.warn("Unable to reconfigure - Log4j has not been initialized."); 258 } 259 } catch (final Exception ex) { 260 LOGGER.error("Error encountered trying to reconfigure logging", ex); 261 } 262 } 263 264 /** 265 * Sets the levels of <code>parentLogger</code> and all 'child' loggers to the given <code>level</code>. 266 * @param parentLogger the parent logger 267 * @param level the new level 268 */ 269 public static void setAllLevels(final String parentLogger, final Level level) { 270 // 1) get logger config 271 // 2) if exact match, use it, if not, create it. 272 // 3) set level on logger config 273 // 4) update child logger configs with level 274 // 5) update loggers 275 final LoggerContext loggerContext = LoggerContext.getContext(false); 276 final Configuration config = loggerContext.getConfiguration(); 277 boolean set = setLevel(parentLogger, level, config); 278 for (final Map.Entry<String, LoggerConfig> entry : config.getLoggers().entrySet()) { 279 if (entry.getKey().startsWith(parentLogger)) { 280 set |= setLevel(entry.getValue(), level); 281 } 282 } 283 if (set) { 284 loggerContext.updateLoggers(); 285 } 286 } 287 288 private static boolean setLevel(final LoggerConfig loggerConfig, final Level level) { 289 final boolean set = !loggerConfig.getLevel().equals(level); 290 if (set) { 291 loggerConfig.setLevel(level); 292 } 293 return set; 294 } 295 296 /** 297 * Sets logger levels. 298 * 299 * @param levelMap 300 * a levelMap where keys are level names and values are new 301 * Levels. 302 */ 303 public static void setLevel(final Map<String, Level> levelMap) { 304 final LoggerContext loggerContext = LoggerContext.getContext(false); 305 final Configuration config = loggerContext.getConfiguration(); 306 boolean set = false; 307 for (final Map.Entry<String, Level> entry : levelMap.entrySet()) { 308 final String loggerName = entry.getKey(); 309 final Level level = entry.getValue(); 310 set |= setLevel(loggerName, level, config); 311 } 312 if (set) { 313 loggerContext.updateLoggers(); 314 } 315 } 316 317 /** 318 * Sets a logger's level. 319 * 320 * @param loggerName 321 * the logger name 322 * @param level 323 * the new level 324 */ 325 public static void setLevel(final String loggerName, final Level level) { 326 final LoggerContext loggerContext = LoggerContext.getContext(false); 327 if (Strings.isEmpty(loggerName)) { 328 setRootLevel(level); 329 } else { 330 if (setLevel(loggerName, level, loggerContext.getConfiguration())) { 331 loggerContext.updateLoggers(); 332 } 333 } 334 } 335 336 private static boolean setLevel(final String loggerName, final Level level, final Configuration config) { 337 boolean set; 338 LoggerConfig loggerConfig = config.getLoggerConfig(loggerName); 339 if (!loggerName.equals(loggerConfig.getName())) { 340 // TODO Should additivity be inherited? 341 loggerConfig = new LoggerConfig(loggerName, level, true); 342 config.addLogger(loggerName, loggerConfig); 343 loggerConfig.setLevel(level); 344 set = true; 345 } else { 346 set = setLevel(loggerConfig, level); 347 } 348 return set; 349 } 350 351 /** 352 * Sets the root logger's level. 353 * 354 * @param level 355 * the new level 356 */ 357 public static void setRootLevel(final Level level) { 358 final LoggerContext loggerContext = LoggerContext.getContext(false); 359 final LoggerConfig loggerConfig = loggerContext.getConfiguration().getRootLogger(); 360 if (!loggerConfig.getLevel().equals(level)) { 361 loggerConfig.setLevel(level); 362 loggerContext.updateLoggers(); 363 } 364 } 365 366 /** 367 * Shuts down the given logger context. This request does not wait for Log4j tasks to complete. 368 * <p> 369 * Log4j starts threads to perform certain actions like file rollovers; calling this method will not wait until the 370 * rollover thread is done. When this method returns, these tasks' status are undefined, the tasks may be done or 371 * not. 372 * </p> 373 * 374 * @param ctx 375 * the logger context to shut down, may be null. 376 */ 377 public static void shutdown(final LoggerContext ctx) { 378 if (ctx != null) { 379 ctx.stop(); 380 } 381 } 382 383 /** 384 * Shuts down the given logger context. 385 * <p> 386 * Log4j can start threads to perform certain actions like file rollovers; calling this method with a positive 387 * timeout will block until the rollover thread is done. 388 * </p> 389 * 390 * @param ctx 391 * the logger context to shut down, may be null. 392 * @param timeout 393 * the maximum time to wait 394 * @param timeUnit 395 * the time unit of the timeout argument 396 * @return {@code true} if the logger context terminated and {@code false} if the timeout elapsed before 397 * termination. 398 * 399 * @see LoggerContext#stop(long, TimeUnit) 400 * 401 * @since 2.7 402 */ 403 public static boolean shutdown(final LoggerContext ctx, final long timeout, final TimeUnit timeUnit) { 404 if (ctx != null) { 405 return ctx.stop(timeout, timeUnit); 406 } 407 return true; 408 } 409 410 private Configurator() { 411 // empty 412 } 413}