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