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