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 } 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 * 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 /** 168 * Initializes the Logging Context. 169 * @param name The Context name. 170 * @param loader The ClassLoader for the Context (or null). 171 * @param configLocation The configuration for the logging context (or null). 172 * @param entry The external context entry to be attached to the LoggerContext 173 * @return The LoggerContext. 174 */ 175 public static LoggerContext initialize(final String name, final ClassLoader loader, final URI configLocation, 176 final Map.Entry<String, Object> entry) { 177 178 try { 179 final Log4jContextFactory factory = getFactory(); 180 return factory == null ? null : 181 factory.getContext(FQCN, loader, entry, false, configLocation, name); 182 } catch (final Exception ex) { 183 LOGGER.error("There was a problem initializing the LoggerContext [{}] using configuration at [{}].", 184 name, configLocation, ex); 185 } 186 return null; 187 } 188 189 public static LoggerContext initialize(final String name, final ClassLoader loader, final List<URI> configLocations, 190 final Object externalContext) { 191 try { 192 final Log4jContextFactory factory = getFactory(); 193 return factory == null ? 194 null : 195 factory.getContext(FQCN, loader, externalContext, false, configLocations, name); 196 } catch (final Exception ex) { 197 LOGGER.error("There was a problem initializing the LoggerContext [{}] using configurations at [{}].", name, 198 configLocations, ex); 199 } 200 return null; 201 } 202 203 /** 204 * Initializes the Logging Context. 205 * @param name The Context name. 206 * @param configLocation The configuration for the logging context. 207 * @return The LoggerContext or null if an error occurred (check the status logger). 208 */ 209 public static LoggerContext initialize(final String name, final String configLocation) { 210 return initialize(name, null, configLocation); 211 } 212 213 /** 214 * Initializes the Logging Context. 215 * @param configuration The Configuration. 216 * @return The LoggerContext. 217 */ 218 public static LoggerContext initialize(final Configuration configuration) { 219 return initialize(null, configuration, null); 220 } 221 222 /** 223 * Initializes the Logging Context. 224 * @param loader The ClassLoader. 225 * @param configuration The Configuration. 226 * @return The LoggerContext. 227 */ 228 public static LoggerContext initialize(final ClassLoader loader, final Configuration configuration) { 229 return initialize(loader, configuration, null); 230 } 231 232 /** 233 * Initializes the Logging Context. 234 * @param loader The ClassLoader. 235 * @param configuration The Configuration. 236 * @param externalContext - The external context to be attached to the LoggerContext. 237 * @return The LoggerContext. 238 */ 239 public static LoggerContext initialize(final ClassLoader loader, final Configuration configuration, final Object externalContext) { 240 try { 241 final Log4jContextFactory factory = getFactory(); 242 return factory == null ? null : 243 factory.getContext(FQCN, loader, externalContext, false, configuration); 244 } catch (final Exception ex) { 245 LOGGER.error("There was a problem initializing the LoggerContext using configuration {}", 246 configuration.getName(), ex); 247 } 248 return null; 249 } 250 251 /** 252 * Reconfigure using an already constructed Configuration. 253 * @param configuration The configuration. 254 * @since 2.13.0 255 */ 256 public static void reconfigure(final Configuration configuration) { 257 try { 258 final Log4jContextFactory factory = getFactory(); 259 if (factory != null) { 260 factory.getContext(FQCN, null, null, false) 261 .reconfigure(configuration); 262 } 263 } catch (final Exception ex) { 264 LOGGER.error("There was a problem initializing the LoggerContext using configuration {}", 265 configuration.getName(), ex); 266 } 267 } 268 269 /** 270 * Reload the existing reconfiguration. 271 * @since 2.12.0 272 */ 273 public static void reconfigure() { 274 try { 275 Log4jContextFactory factory = getFactory(); 276 if (factory != null) { 277 factory.getSelector().getContext(FQCN, null, false).reconfigure(); 278 } else { 279 LOGGER.warn("Unable to reconfigure - Log4j has not been initialized."); 280 } 281 } catch (final Exception ex) { 282 LOGGER.error("Error encountered trying to reconfigure logging", ex); 283 } 284 } 285 286 /** 287 * Reconfigure with a potentially new configuration. 288 * @param uri The location of the configuration. 289 * @since 2.12.0 290 */ 291 public static void reconfigure(final URI uri) { 292 try { 293 Log4jContextFactory factory = getFactory(); 294 if (factory != null) { 295 factory.getSelector().getContext(FQCN, null, false).setConfigLocation(uri); 296 } else { 297 LOGGER.warn("Unable to reconfigure - Log4j has not been initialized."); 298 } 299 } catch (final Exception ex) { 300 LOGGER.error("Error encountered trying to reconfigure logging", ex); 301 } 302 } 303 304 /** 305 * Sets the levels of <code>parentLogger</code> and all 'child' loggers to the given <code>level</code>. 306 * @param parentLogger the parent logger 307 * @param level the new level 308 */ 309 public static void setAllLevels(final String parentLogger, final Level level) { 310 // 1) get logger config 311 // 2) if exact match, use it, if not, create it. 312 // 3) set level on logger config 313 // 4) update child logger configs with level 314 // 5) update loggers 315 final LoggerContext loggerContext = LoggerContext.getContext(false); 316 final Configuration config = loggerContext.getConfiguration(); 317 boolean set = setLevel(parentLogger, level, config); 318 for (final Map.Entry<String, LoggerConfig> entry : config.getLoggers().entrySet()) { 319 if (entry.getKey().startsWith(parentLogger)) { 320 set |= setLevel(entry.getValue(), level); 321 } 322 } 323 if (set) { 324 loggerContext.updateLoggers(); 325 } 326 } 327 328 private static boolean setLevel(final LoggerConfig loggerConfig, final Level level) { 329 final boolean set = !loggerConfig.getLevel().equals(level); 330 if (set) { 331 loggerConfig.setLevel(level); 332 } 333 return set; 334 } 335 336 /** 337 * Sets logger levels. 338 * 339 * @param levelMap 340 * a levelMap where keys are level names and values are new 341 * Levels. 342 */ 343 public static void setLevel(final Map<String, Level> levelMap) { 344 final LoggerContext loggerContext = LoggerContext.getContext(false); 345 final Configuration config = loggerContext.getConfiguration(); 346 boolean set = false; 347 for (final Map.Entry<String, Level> entry : levelMap.entrySet()) { 348 final String loggerName = entry.getKey(); 349 final Level level = entry.getValue(); 350 set |= setLevel(loggerName, level, config); 351 } 352 if (set) { 353 loggerContext.updateLoggers(); 354 } 355 } 356 357 /** 358 * Sets a logger's level. 359 * 360 * @param loggerName 361 * the logger name 362 * @param level 363 * the new level 364 */ 365 public static void setLevel(final String loggerName, final Level level) { 366 final LoggerContext loggerContext = LoggerContext.getContext(false); 367 if (Strings.isEmpty(loggerName)) { 368 setRootLevel(level); 369 } else if (setLevel(loggerName, level, loggerContext.getConfiguration())) { 370 loggerContext.updateLoggers(); 371 } 372 } 373 374 private static boolean setLevel(final String loggerName, final Level level, final Configuration config) { 375 boolean set; 376 LoggerConfig loggerConfig = config.getLoggerConfig(loggerName); 377 if (!loggerName.equals(loggerConfig.getName())) { 378 // TODO Should additivity be inherited? 379 loggerConfig = new LoggerConfig(loggerName, level, true); 380 config.addLogger(loggerName, loggerConfig); 381 loggerConfig.setLevel(level); 382 set = true; 383 } else { 384 set = setLevel(loggerConfig, level); 385 } 386 return set; 387 } 388 389 /** 390 * Sets the root logger's level. 391 * 392 * @param level 393 * the new level 394 */ 395 public static void setRootLevel(final Level level) { 396 final LoggerContext loggerContext = LoggerContext.getContext(false); 397 final LoggerConfig loggerConfig = loggerContext.getConfiguration().getRootLogger(); 398 if (!loggerConfig.getLevel().equals(level)) { 399 loggerConfig.setLevel(level); 400 loggerContext.updateLoggers(); 401 } 402 } 403 404 /** 405 * Shuts down the given logger context. This request does not wait for Log4j tasks to complete. 406 * <p> 407 * Log4j starts threads to perform certain actions like file rollovers; calling this method will not wait until the 408 * rollover thread is done. When this method returns, these tasks' status are undefined, the tasks may be done or 409 * not. 410 * </p> 411 * 412 * @param ctx 413 * the logger context to shut down, may be null. 414 */ 415 public static void shutdown(final LoggerContext ctx) { 416 if (ctx != null) { 417 ctx.stop(); 418 } 419 } 420 421 /** 422 * Shuts down the given logger context. 423 * <p> 424 * Log4j can start threads to perform certain actions like file rollovers; calling this method with a positive 425 * timeout will block until the rollover thread is done. 426 * </p> 427 * 428 * @param ctx 429 * the logger context to shut down, may be null. 430 * @param timeout 431 * the maximum time to wait 432 * @param timeUnit 433 * the time unit of the timeout argument 434 * @return {@code true} if the logger context terminated and {@code false} if the timeout elapsed before 435 * termination. 436 * 437 * @see LoggerContext#stop(long, TimeUnit) 438 * 439 * @since 2.7 440 */ 441 public static boolean shutdown(final LoggerContext ctx, final long timeout, final TimeUnit timeUnit) { 442 if (ctx != null) { 443 return ctx.stop(timeout, timeUnit); 444 } 445 return true; 446 } 447 448 private Configurator() { 449 // empty 450 } 451}