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