001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * <p> 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * <p> 009 * or (per the licensee's choosing) 010 * <p> 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.classic; 015 016import static ch.qos.logback.core.CoreConstants.EVALUATOR_MAP; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.concurrent.ConcurrentHashMap; 025import java.util.concurrent.ScheduledFuture; 026import java.util.concurrent.locks.ReentrantLock; 027 028import ch.qos.logback.classic.util.LogbackMDCAdapter; 029import ch.qos.logback.core.status.ErrorStatus; 030import ch.qos.logback.core.status.InfoStatus; 031import org.slf4j.ILoggerFactory; 032import org.slf4j.Marker; 033 034import ch.qos.logback.classic.spi.LoggerComparator; 035import ch.qos.logback.classic.spi.LoggerContextListener; 036import ch.qos.logback.classic.spi.LoggerContextVO; 037import ch.qos.logback.classic.spi.TurboFilterList; 038import ch.qos.logback.classic.turbo.TurboFilter; 039import ch.qos.logback.classic.util.LoggerNameUtil; 040import ch.qos.logback.core.ContextBase; 041import ch.qos.logback.core.boolex.EventEvaluator; 042import ch.qos.logback.core.spi.FilterReply; 043import ch.qos.logback.core.spi.LifeCycle; 044import ch.qos.logback.core.spi.SequenceNumberGenerator; 045import ch.qos.logback.core.status.StatusListener; 046import ch.qos.logback.core.status.StatusManager; 047import ch.qos.logback.core.status.WarnStatus; 048import org.slf4j.spi.MDCAdapter; 049 050/** 051 * LoggerContext glues many of the logback-classic components together. In 052 * principle, every logback-classic component instance is attached either 053 * directly or indirectly to a LoggerContext instance. Just as importantly 054 * LoggerContext implements the {@link ILoggerFactory} acting as the 055 * manufacturing source of {@link Logger} instances. 056 * 057 * @author Ceki Gulcu 058 */ 059public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle { 060 061 /** 062 * Default setting of packaging data in stack traces 063 */ 064 public static final boolean DEFAULT_PACKAGING_DATA = false; 065 066 final Logger root; 067 private int size; 068 private int noAppenderWarning = 0; 069 final private List<LoggerContextListener> loggerContextListenerList = new ArrayList<LoggerContextListener>(); 070 071 private Map<String, Logger> loggerCache; 072 073 private LoggerContextVO loggerContextRemoteView; 074 private final TurboFilterList turboFilterList = new TurboFilterList(); 075 private boolean packagingDataEnabled = DEFAULT_PACKAGING_DATA; 076 SequenceNumberGenerator sequenceNumberGenerator = null; // by default there is no SequenceNumberGenerator 077 078 MDCAdapter mdcAdapter; 079 080 private int maxCallerDataDepth = ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH; 081 082 int resetCount = 0; 083 private List<String> frameworkPackages; 084 085 public LoggerContext() { 086 super(); 087 this.loggerCache = new ConcurrentHashMap<String, Logger>(); 088 089 this.loggerContextRemoteView = new LoggerContextVO(this); 090 this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this); 091 this.root.setLevel(Level.DEBUG); 092 loggerCache.put(Logger.ROOT_LOGGER_NAME, root); 093 initEvaluatorMap(); 094 size = 1; 095 this.frameworkPackages = new ArrayList<String>(); 096 // In 1.5.7, the stop() method assumes that at some point the context has been started 097 // since earlier versions of logback did not mandate calling the start method 098 // we need to call in the constructor 099 this.start(); 100 } 101 102 void initEvaluatorMap() { 103 putObject(EVALUATOR_MAP, new HashMap<String, EventEvaluator<?>>()); 104 } 105 106 /** 107 * A new instance of LoggerContextRemoteView needs to be created each time the 108 * name or propertyMap (including keys or values) changes. 109 */ 110 private void updateLoggerContextVO() { 111 loggerContextRemoteView = new LoggerContextVO(this); 112 } 113 114 @Override 115 public void putProperty(String key, String val) { 116 super.putProperty(key, val); 117 updateLoggerContextVO(); 118 } 119 120 @Override 121 public void setName(String name) { 122 super.setName(name); 123 updateLoggerContextVO(); 124 } 125 126 public final Logger getLogger(final Class<?> clazz) { 127 return getLogger(clazz.getName()); 128 } 129 130 @Override 131 public Logger getLogger(final String name) { 132 133 if (name == null) { 134 throw new IllegalArgumentException("name argument cannot be null"); 135 } 136 137 // if we are asking for the root logger, then let us return it without 138 // wasting time 139 if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) { 140 return root; 141 } 142 143 int i = 0; 144 Logger logger = root; 145 146 // check if the desired logger exists, if it does, return it 147 // without further ado. 148 Logger childLogger = (Logger) loggerCache.get(name); 149 // if we have the child, then let us return it without wasting time 150 if (childLogger != null) { 151 return childLogger; 152 } 153 154 // if the desired logger does not exist, them create all the loggers 155 // in between as well (if they don't already exist) 156 String childName; 157 while (true) { 158 int h = LoggerNameUtil.getSeparatorIndexOf(name, i); 159 if (h == -1) { 160 childName = name; 161 } else { 162 childName = name.substring(0, h); 163 } 164 // move i left of the last point 165 i = h + 1; 166 synchronized (logger) { 167 childLogger = logger.getChildByName(childName); 168 if (childLogger == null) { 169 childLogger = logger.createChildByName(childName); 170 loggerCache.put(childName, childLogger); 171 incSize(); 172 } 173 } 174 logger = childLogger; 175 if (h == -1) { 176 return childLogger; 177 } 178 } 179 } 180 181 private void incSize() { 182 size++; 183 } 184 185 int size() { 186 return size; 187 } 188 189 /** 190 * Check if the named logger exists in the hierarchy. If so return its 191 * reference, otherwise returns <code>null</code>. 192 * 193 * @param name the name of the logger to search for. 194 */ 195 public Logger exists(String name) { 196 return (Logger) loggerCache.get(name); 197 } 198 199 final void noAppenderDefinedWarning(final Logger logger) { 200 if (noAppenderWarning++ == 0) { 201 getStatusManager().add(new WarnStatus("No appenders present in context [" + getName() + "] for logger [" + logger.getName() + "].", logger)); 202 } 203 } 204 205 public List<Logger> getLoggerList() { 206 Collection<Logger> collection = loggerCache.values(); 207 List<Logger> loggerList = new ArrayList<Logger>(collection); 208 Collections.sort(loggerList, new LoggerComparator()); 209 return loggerList; 210 } 211 212 public LoggerContextVO getLoggerContextRemoteView() { 213 return loggerContextRemoteView; 214 } 215 216 public void setPackagingDataEnabled(boolean packagingDataEnabled) { 217 this.packagingDataEnabled = packagingDataEnabled; 218 } 219 220 public boolean isPackagingDataEnabled() { 221 return packagingDataEnabled; 222 } 223 224 void cancelScheduledTasks() { 225 226 try { 227 configurationLock.lock(); 228 229 for (ScheduledFuture<?> sf : scheduledFutures) { 230 sf.cancel(false); 231 } 232 scheduledFutures.clear(); 233 } finally { 234 configurationLock.unlock(); 235 } 236 } 237 238 private void resetStatusListenersExceptResetResistant() { 239 StatusManager sm = getStatusManager(); 240 for (StatusListener sl : sm.getCopyOfStatusListenerList()) { 241 if (!sl.isResetResistant()) { 242 sm.remove(sl); 243 } 244 } 245 } 246 247 public TurboFilterList getTurboFilterList() { 248 return turboFilterList; 249 } 250 251 public void addTurboFilter(TurboFilter newFilter) { 252 turboFilterList.add(newFilter); 253 } 254 255 /** 256 * First processPriorToRemoval all registered turbo filters and then clear the 257 * registration list. 258 */ 259 public void resetTurboFilterList() { 260 for (TurboFilter tf : turboFilterList) { 261 tf.stop(); 262 } 263 turboFilterList.clear(); 264 } 265 266 final FilterReply getTurboFilterChainDecision_0_3OrMore(final Marker marker, final Logger logger, final Level level, final String format, 267 final Object[] params, final Throwable t) { 268 if (turboFilterList.size() == 0) { 269 return FilterReply.NEUTRAL; 270 } 271 return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, params, t); 272 } 273 274 final FilterReply getTurboFilterChainDecision_1(final Marker marker, final Logger logger, final Level level, final String format, final Object param, 275 final Throwable t) { 276 if (turboFilterList.size() == 0) { 277 return FilterReply.NEUTRAL; 278 } 279 return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, new Object[] { param }, t); 280 } 281 282 final FilterReply getTurboFilterChainDecision_2(final Marker marker, final Logger logger, final Level level, final String format, final Object param1, 283 final Object param2, final Throwable t) { 284 if (turboFilterList.size() == 0) { 285 return FilterReply.NEUTRAL; 286 } 287 return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, new Object[] { param1, param2 }, t); 288 } 289 290 // === start listeners ============================================== 291 public void addListener(LoggerContextListener listener) { 292 loggerContextListenerList.add(listener); 293 } 294 295 public void removeListener(LoggerContextListener listener) { 296 loggerContextListenerList.remove(listener); 297 } 298 299 private void resetListenersExceptResetResistant() { 300 List<LoggerContextListener> toRetain = new ArrayList<LoggerContextListener>(); 301 302 for (LoggerContextListener lcl : loggerContextListenerList) { 303 if (lcl.isResetResistant()) { 304 toRetain.add(lcl); 305 } 306 } 307 loggerContextListenerList.retainAll(toRetain); 308 } 309 310 private void resetAllListeners() { 311 loggerContextListenerList.clear(); 312 } 313 314 public List<LoggerContextListener> getCopyOfListenerList() { 315 return new ArrayList<LoggerContextListener>(loggerContextListenerList); 316 } 317 318 void fireOnLevelChange(Logger logger, Level level) { 319 for (LoggerContextListener listener : loggerContextListenerList) { 320 listener.onLevelChange(logger, level); 321 } 322 } 323 324 private void fireOnReset() { 325 for (LoggerContextListener listener : loggerContextListenerList) { 326 listener.onReset(this); 327 } 328 } 329 330 private void fireOnStart() { 331 for (LoggerContextListener listener : loggerContextListenerList) { 332 listener.onStart(this); 333 } 334 } 335 336 private void fireOnStop() { 337 for (LoggerContextListener listener : loggerContextListenerList) { 338 listener.onStop(this); 339 } 340 } 341 342 // === end listeners ============================================== 343 344 @Override 345 public void start() { 346 super.start(); 347 fireOnStart(); 348 } 349 350 public void stop() { 351 if (!isStarted()) 352 return; 353 354 try { 355 configurationLock.lock(); 356 if (!isStarted()) 357 return; 358 359 reset(); 360 fireOnStop(); 361 resetAllListeners(); 362 super.stop(); 363 } finally { 364 configurationLock.unlock(); 365 } 366 } 367 368 /** 369 * This method clears all internal properties, except internal status messages, 370 * closes all appenders, removes any turboFilters, fires an OnReset event, 371 * removes all status listeners, removes all context listeners (except those 372 * which are reset resistant). 373 * <p/> 374 * As mentioned above, internal status messages survive resets. 375 */ 376 @Override 377 public void reset() { 378 resetCount++; 379 super.reset(); 380 initEvaluatorMap(); 381 initCollisionMaps(); 382 root.recursiveReset(); 383 resetTurboFilterList(); 384 cancelScheduledTasks(); 385 fireOnReset(); 386 resetListenersExceptResetResistant(); 387 resetStatusListenersExceptResetResistant(); 388 } 389 390 @Override 391 public String toString() { 392 return this.getClass().getName() + "[" + getName() + "]"; 393 } 394 395 public int getMaxCallerDataDepth() { 396 return maxCallerDataDepth; 397 } 398 399 public void setMaxCallerDataDepth(int maxCallerDataDepth) { 400 this.maxCallerDataDepth = maxCallerDataDepth; 401 } 402 403 /** 404 * List of packages considered part of the logging framework such that they are 405 * never considered as callers of the logging framework. This list used to 406 * compute the caller for logging events. 407 * <p/> 408 * To designate package "com.foo" as well as all its subpackages as being part 409 * of the logging framework, simply add "com.foo" to this list. 410 * 411 * @return list of framework packages 412 */ 413 public List<String> getFrameworkPackages() { 414 return frameworkPackages; 415 } 416 417 @Override 418 public void setSequenceNumberGenerator(SequenceNumberGenerator sng) { 419 this.sequenceNumberGenerator = sng; 420 } 421 422 @Override 423 public SequenceNumberGenerator getSequenceNumberGenerator() { 424 return sequenceNumberGenerator; 425 } 426 427 public MDCAdapter getMDCAdapter() { 428 return mdcAdapter; 429 } 430 431 public void setMDCAdapter(MDCAdapter anAdapter) { 432 if (this.mdcAdapter != null) { 433 StatusManager sm = getStatusManager(); 434 sm.add(new WarnStatus("mdcAdapter being reset a second time", this)); 435 } 436 this.mdcAdapter = anAdapter; 437 } 438}