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 org.apache.logging.log4j.Level; 020import org.apache.logging.log4j.LogManager; 021import org.apache.logging.log4j.Logger; 022import org.apache.logging.log4j.core.LoggerContext; 023import org.apache.logging.log4j.core.impl.Log4jContextFactory; 024import org.apache.logging.log4j.core.util.NetUtils; 025import org.apache.logging.log4j.spi.LoggerContextFactory; 026import org.apache.logging.log4j.status.StatusLogger; 027import org.apache.logging.log4j.util.Strings; 028 029import java.net.URI; 030import java.util.ArrayList; 031import java.util.List; 032import java.util.Map; 033import java.util.concurrent.TimeUnit; 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 { 050 if (factory != null) { 051 LOGGER.error("LogManager returned an instance of {} which does not implement {}. Unable to initialize Log4j.", 052 factory.getClass().getName(), Log4jContextFactory.class.getName()); 053 } else { 054 LOGGER.fatal("LogManager did not return a LoggerContextFactory. This indicates something has gone terribly wrong!"); 055 } 056 return null; 057 } 058 } 059 060 /** 061 * Initializes the Logging Context. 062 * @param loader The ClassLoader for the Context (or null). 063 * @param source The InputSource for the configuration. 064 * @return The LoggerContext. 065 */ 066 public static LoggerContext initialize(final ClassLoader loader, 067 final ConfigurationSource source) { 068 return initialize(loader, source, null); 069 } 070 071 /** 072 * Initializes the Logging Context. 073 * @param loader The ClassLoader for the Context (or null). 074 * @param source The InputSource for the configuration. 075 * @param externalContext The external context to be attached to the LoggerContext. 076 * @return The LoggerContext. 077 */ 078 079 public static LoggerContext initialize(final ClassLoader loader, 080 final ConfigurationSource source, 081 final Object externalContext) 082 { 083 084 try { 085 final Log4jContextFactory factory = getFactory(); 086 return factory == null ? null : 087 factory.getContext(FQCN, loader, externalContext, false, source); 088 } catch (final Exception ex) { 089 LOGGER.error("There was a problem obtaining a LoggerContext using the configuration source [{}]", source, ex); 090 } 091 return null; 092 } 093 094 /** 095 * Initializes the Logging Context. 096 * @param name The Context name. 097 * @param loader The ClassLoader for the Context (or null). 098 * @param configLocation The configuration for the logging context. 099 * @return The LoggerContext or null if an error occurred (check the status logger). 100 */ 101 public static LoggerContext initialize(final String name, final ClassLoader loader, final String configLocation) { 102 return initialize(name, loader, configLocation, null); 103 104 } 105 106 /** 107 * Initializes the Logging Context. 108 * @param name The Context name. 109 * @param loader The ClassLoader for the Context (or null). 110 * @param configLocation The configuration for the logging context (or null, or blank). 111 * @param externalContext The external context to be attached to the LoggerContext 112 * @return The LoggerContext or null if an error occurred (check the status logger). 113 */ 114 public static LoggerContext initialize(final String name, final ClassLoader loader, final String configLocation, 115 final Object externalContext) { 116 if (Strings.isBlank(configLocation)) { 117 return initialize(name, loader, (URI) null, externalContext); 118 } 119 if (configLocation.contains(",")) { 120 final String[] parts = configLocation.split(","); 121 String scheme = null; 122 final List<URI> uris = new ArrayList<>(parts.length); 123 for (final String part : parts) { 124 final URI uri = NetUtils.toURI(scheme != null ? scheme + ":" + part.trim() : part.trim()); 125 if (scheme == null && uri.getScheme() != null) { 126 scheme = uri.getScheme(); 127 } 128 uris.add(uri); 129 } 130 return initialize(name, loader, uris, externalContext); 131 } 132 return initialize(name, loader, NetUtils.toURI(configLocation), externalContext); 133 } 134 135 /** 136 * Initializes the Logging Context. 137 * @param name The Context name. 138 * @param loader The ClassLoader for the Context (or null). 139 * @param configLocation The configuration for the logging context. 140 * @return The LoggerContext. 141 */ 142 public static LoggerContext initialize(final String name, final ClassLoader loader, final URI configLocation) { 143 return initialize(name, loader, configLocation, null); 144 } 145 146 /** 147 * Initializes the Logging Context. 148 * @param name The Context name. 149 * @param loader The ClassLoader for the Context (or null). 150 * @param configLocation The configuration for the logging context (or null). 151 * @param externalContext The external context to be attached to the LoggerContext 152 * @return The LoggerContext. 153 */ 154 public static LoggerContext initialize(final String name, final ClassLoader loader, final URI configLocation, 155 final Object externalContext) { 156 157 try { 158 final Log4jContextFactory factory = getFactory(); 159 return factory == null ? null : 160 factory.getContext(FQCN, loader, externalContext, false, configLocation, name); 161 } catch (final Exception ex) { 162 LOGGER.error("There was a problem initializing the LoggerContext [{}] using configuration at [{}].", 163 name, configLocation, ex); 164 } 165 return null; 166 } 167 168 /** 169 * Initializes the Logging Context. 170 * @param name The Context name. 171 * @param loader The ClassLoader for the Context (or null). 172 * @param configLocation The configuration for the logging context (or null). 173 * @param entry The external context entry to be attached to the LoggerContext 174 * @return The LoggerContext. 175 */ 176 public static LoggerContext initialize(final String name, final ClassLoader loader, final URI configLocation, 177 final Map.Entry<String, Object> entry) { 178 179 try { 180 final Log4jContextFactory factory = getFactory(); 181 return factory == null ? null : 182 factory.getContext(FQCN, loader, entry, false, configLocation, name); 183 } catch (final Exception ex) { 184 LOGGER.error("There was a problem initializing the LoggerContext [{}] using configuration at [{}].", 185 name, configLocation, ex); 186 } 187 return null; 188 } 189 190 public static LoggerContext initialize(final String name, final ClassLoader loader, final List<URI> configLocations, 191 final Object externalContext) { 192 try { 193 final Log4jContextFactory factory = getFactory(); 194 return factory == null ? 195 null : 196 factory.getContext(FQCN, loader, externalContext, false, configLocations, name); 197 } catch (final Exception ex) { 198 LOGGER.error("There was a problem initializing the LoggerContext [{}] using configurations at [{}].", name, 199 configLocations, ex); 200 } 201 return null; 202 } 203 204 /** 205 * Initializes the Logging Context. 206 * @param name The Context name. 207 * @param configLocation The configuration for the logging context. 208 * @return The LoggerContext or null if an error occurred (check the status logger). 209 */ 210 public static LoggerContext initialize(final String name, final String configLocation) { 211 return initialize(name, null, configLocation); 212 } 213 214 /** 215 * Initializes the Logging Context. 216 * @param configuration The Configuration. 217 * @return The LoggerContext. 218 */ 219 public static LoggerContext initialize(final Configuration configuration) { 220 return initialize(null, configuration, null); 221 } 222 223 /** 224 * Initializes the Logging Context. 225 * @param loader The ClassLoader. 226 * @param configuration The Configuration. 227 * @return The LoggerContext. 228 */ 229 public static LoggerContext initialize(final ClassLoader loader, final Configuration configuration) { 230 return initialize(loader, configuration, null); 231 } 232 233 /** 234 * Initializes the Logging Context. 235 * @param loader The ClassLoader. 236 * @param configuration The Configuration. 237 * @param externalContext - The external context to be attached to the LoggerContext. 238 * @return The LoggerContext. 239 */ 240 public static LoggerContext initialize(final ClassLoader loader, final Configuration configuration, final Object externalContext) { 241 try { 242 final Log4jContextFactory factory = getFactory(); 243 return factory == null ? null : 244 factory.getContext(FQCN, loader, externalContext, false, configuration); 245 } catch (final Exception ex) { 246 LOGGER.error("There was a problem initializing the LoggerContext using configuration {}", 247 configuration.getName(), ex); 248 } 249 return null; 250 } 251 252 /** 253 * Reconfigure using an already constructed Configuration. 254 * @param configuration The configuration. 255 * @since 2.13.0 256 */ 257 public static void reconfigure(final Configuration configuration) { 258 try { 259 final Log4jContextFactory factory = getFactory(); 260 if (factory != null) { 261 factory.getContext(FQCN, null, null, false) 262 .reconfigure(configuration); 263 } 264 } catch (final Exception ex) { 265 LOGGER.error("There was a problem initializing the LoggerContext using configuration {}", 266 configuration.getName(), ex); 267 } 268 } 269 270 /** 271 * Reload the existing reconfiguration. 272 * @since 2.12.0 273 */ 274 public static void reconfigure() { 275 try { 276 Log4jContextFactory factory = getFactory(); 277 if (factory != null) { 278 factory.getSelector().getContext(FQCN, null, false).reconfigure(); 279 } else { 280 LOGGER.warn("Unable to reconfigure - Log4j has not been initialized."); 281 } 282 } catch (final Exception ex) { 283 LOGGER.error("Error encountered trying to reconfigure logging", ex); 284 } 285 } 286 287 /** 288 * Reconfigure with a potentially new configuration. 289 * @param uri The location of the configuration. 290 * @since 2.12.0 291 */ 292 public static void reconfigure(final URI uri) { 293 try { 294 Log4jContextFactory factory = getFactory(); 295 if (factory != null) { 296 factory.getSelector().getContext(FQCN, null, false).setConfigLocation(uri); 297 } else { 298 LOGGER.warn("Unable to reconfigure - Log4j has not been initialized."); 299 } 300 } catch (final Exception ex) { 301 LOGGER.error("Error encountered trying to reconfigure logging", ex); 302 } 303 } 304 305 /** 306 * Sets the levels of <code>parentLogger</code> and all 'child' loggers to the given <code>level</code>. 307 * @param parentLogger the parent logger 308 * @param level the new level 309 */ 310 public static void setAllLevels(final String parentLogger, final Level level) { 311 // 1) get logger config 312 // 2) if exact match, use it, if not, create it. 313 // 3) set level on logger config 314 // 4) update child logger configs with level 315 // 5) update loggers 316 final LoggerContext loggerContext = LoggerContext.getContext(false); 317 final Configuration config = loggerContext.getConfiguration(); 318 boolean set = setLevel(parentLogger, level, config); 319 for (final Map.Entry<String, LoggerConfig> entry : config.getLoggers().entrySet()) { 320 if (entry.getKey().startsWith(parentLogger)) { 321 set |= setLevel(entry.getValue(), level); 322 } 323 } 324 if (set) { 325 loggerContext.updateLoggers(); 326 } 327 } 328 329 private static boolean setLevel(final LoggerConfig loggerConfig, final Level level) { 330 final boolean set = !loggerConfig.getLevel().equals(level); 331 if (set) { 332 loggerConfig.setLevel(level); 333 } 334 return set; 335 } 336 337 /** 338 * Sets logger levels. 339 * 340 * @param levelMap 341 * a levelMap where keys are level names and values are new 342 * Levels. 343 */ 344 public static void setLevel(final Map<String, Level> levelMap) { 345 final LoggerContext loggerContext = LoggerContext.getContext(false); 346 final Configuration config = loggerContext.getConfiguration(); 347 boolean set = false; 348 for (final Map.Entry<String, Level> entry : levelMap.entrySet()) { 349 final String loggerName = entry.getKey(); 350 final Level level = entry.getValue(); 351 set |= setLevel(loggerName, level, config); 352 } 353 if (set) { 354 loggerContext.updateLoggers(); 355 } 356 } 357 358 /** 359 * Sets a logger's level. 360 * 361 * @param loggerName 362 * the logger name 363 * @param level 364 * the new level 365 */ 366 public static void setLevel(final String loggerName, final Level level) { 367 final LoggerContext loggerContext = LoggerContext.getContext(false); 368 if (Strings.isEmpty(loggerName)) { 369 setRootLevel(level); 370 } else if (setLevel(loggerName, level, loggerContext.getConfiguration())) { 371 loggerContext.updateLoggers(); 372 } 373 } 374 375 private static boolean setLevel(final String loggerName, final Level level, final Configuration config) { 376 boolean set; 377 LoggerConfig loggerConfig = config.getLoggerConfig(loggerName); 378 if (!loggerName.equals(loggerConfig.getName())) { 379 // TODO Should additivity be inherited? 380 loggerConfig = new LoggerConfig(loggerName, level, true); 381 config.addLogger(loggerName, loggerConfig); 382 loggerConfig.setLevel(level); 383 set = true; 384 } else { 385 set = setLevel(loggerConfig, level); 386 } 387 return set; 388 } 389 390 /** 391 * Sets the root logger's level. 392 * 393 * @param level 394 * the new level 395 */ 396 public static void setRootLevel(final Level level) { 397 final LoggerContext loggerContext = LoggerContext.getContext(false); 398 final LoggerConfig loggerConfig = loggerContext.getConfiguration().getRootLogger(); 399 if (!loggerConfig.getLevel().equals(level)) { 400 loggerConfig.setLevel(level); 401 loggerContext.updateLoggers(); 402 } 403 } 404 405 /** 406 * Shuts down the given logger context. This request does not wait for Log4j tasks to complete. 407 * <p> 408 * Log4j starts threads to perform certain actions like file rollovers; calling this method will not wait until the 409 * rollover thread is done. When this method returns, these tasks' status are undefined, the tasks may be done or 410 * not. 411 * </p> 412 * 413 * @param ctx 414 * the logger context to shut down, may be null. 415 */ 416 public static void shutdown(final LoggerContext ctx) { 417 if (ctx != null) { 418 ctx.stop(); 419 } 420 } 421 422 /** 423 * Shuts down the given logger context. 424 * <p> 425 * Log4j can start threads to perform certain actions like file rollovers; calling this method with a positive 426 * timeout will block until the rollover thread is done. 427 * </p> 428 * 429 * @param ctx 430 * the logger context to shut down, may be null. 431 * @param timeout 432 * the maximum time to wait 433 * @param timeUnit 434 * the time unit of the timeout argument 435 * @return {@code true} if the logger context terminated and {@code false} if the timeout elapsed before 436 * termination. 437 * 438 * @see LoggerContext#stop(long, TimeUnit) 439 * 440 * @since 2.7 441 */ 442 public static boolean shutdown(final LoggerContext ctx, final long timeout, final TimeUnit timeUnit) { 443 if (ctx != null) { 444 return ctx.stop(timeout, timeUnit); 445 } 446 return true; 447 } 448 449 private Configurator() { 450 // empty 451 } 452}