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