/**
* Logback: the reliable, generic, fast and flexible logging framework.
*
* Copyright (C) 1999-2006, QOS.ch
*
* This library is free software, you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation.
*/
package ch.qos.logback.classic;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import org.slf4j.ILoggerFactory;
import org.slf4j.Marker;
import ch.qos.logback.classic.spi.ContextListener;
import ch.qos.logback.classic.spi.LoggerComparator;
import ch.qos.logback.classic.spi.LoggerContextRemoteView;
import ch.qos.logback.classic.spi.TurboFilterAttachable;
import ch.qos.logback.classic.spi.TurboFilterAttachableImpl;
import ch.qos.logback.classic.turbo.TurboFilter;
import ch.qos.logback.core.ContextBase;
import ch.qos.logback.core.CoreGlobal;
import ch.qos.logback.core.spi.FilterReply;
import ch.qos.logback.core.spi.LifeCycle;
import ch.qos.logback.core.status.ErrorStatus;
/**
* LoggerContext glues many of the logback-classic components together. In principle,
* every logback-classic component instance is attached either directly or indirecty
* to a LoggerContext instance. Just as importantly LoggerContext implements the
* {@link ILoggerFactory} acting as the manufacturing source of {@link Logger}
* instances.
*
* @author Ceki Gulcu
*/
public class LoggerContext extends ContextBase implements ILoggerFactory,
TurboFilterAttachable, LifeCycle {
public static final String ROOT_NAME = "root";
final Logger root;
private int size;
private int noAppenderWarning = 0;
final private List<ContextListener> contextListenerList = new ArrayList<ContextListener>();
// We want loggerCache to be synchronized so Hashtable is a good choice. In
// practice, it performs a little faster than the map returned by
// Collections.synchronizedMap at the
// cost of a very slightly higher memory footprint.
private Hashtable<String, Logger> loggerCache;
LoggerContextRemoteView loggerContextRemoteView;
TurboFilterAttachableImpl cfai = null;
boolean started = false;
public LoggerContext() {
super();
this.loggerCache = new Hashtable<String, Logger>();
this.loggerContextRemoteView = new LoggerContextRemoteView(this);
this.root = new Logger(ROOT_NAME, null, this);
this.root.setLevel(Level.DEBUG);
loggerCache.put(ROOT_NAME, root);
putObject(CoreGlobal.EVALUATOR_MAP, new HashMap());
size = 1;
}
/**
* A new instance of LoggerContextRemoteView needs to be created each time the
* name or propertyMap (including keys or values) changes.
*/
private void syncRemoteView() {
loggerContextRemoteView = new LoggerContextRemoteView(this);
for (Logger logger : loggerCache.values()) {
logger.buildRemoteView();
}
}
@Override
public void putProperty(String key, String val) {
super.putProperty(key, val);
syncRemoteView();
}
@Override
public void setName(String name) {
super.setName(name);
syncRemoteView();
}
public final Logger getLogger(final Class clazz) {
return getLogger(clazz.getName());
}
public final Logger getLogger(final String name) {
// if we are asking for the root logger, then let us return it without
// wasting time
if (ROOT_NAME.equalsIgnoreCase(name)) {
return root;
}
int i = 0;
Logger logger = root;
// check if the desired logger exists, if it does, return it
// without further ado.
Logger childLogger = (Logger) loggerCache.get(name);
// if we have the child, then let us return it without wasting time
if (childLogger != null) {
return childLogger;
}
// if the desired logger does not exist, them create all the loggers
// in between as well (if they don't already exist)
String childName;
while (true) {
int h = name.indexOf('.', i);
if (h == -1) {
childName = name;
} else {
childName = name.substring(0, h);
}
// move i left of the last point
i = h + 1;
synchronized (logger) {
childLogger = logger.getChildByName(childName);
if (childLogger == null) {
childLogger = logger.createChildByName(childName);
loggerCache.put(childName, childLogger);
incSize();
}
}
logger = childLogger;
if (h == -1) {
return childLogger;
}
}
}
private synchronized void incSize() {
size++;
}
synchronized int size() {
return size;
}
/**
* Check if the named logger exists in the hierarchy. If so return its
* reference, otherwise returns <code>null</code>.
*
* @param name
* the name of the logger to search for.
*/
public Logger exists(String name) {
return (Logger) loggerCache.get(name);
}
final void noAppenderDefinedWarning(final Logger logger) {
if (noAppenderWarning++ == 0) {
getStatusManager().add(
new ErrorStatus("No appenders present in context [" + getName()
+ "] for logger [" + logger.getName() + "].", logger));
}
}
public List<Logger> getLoggerList() {
Collection<Logger> collection = loggerCache.values();
List<Logger> loggerList = new ArrayList<Logger>(collection);
Collections.sort(loggerList, new LoggerComparator());
return loggerList;
}
public LoggerContextRemoteView getLoggerContextRemoteView() {
return loggerContextRemoteView;
}
public void shutdownAndReset() {
root.recursiveReset();
fireOnReset();
}
public void addTurboFilter(TurboFilter newFilter) {
if (cfai == null) {
cfai = new TurboFilterAttachableImpl();
}
cfai.addTurboFilter(newFilter);
}
public void clearAllTurboFilters() {
if (cfai == null) {
return;
}
cfai.clearAllTurboFilters();
cfai = null;
}
final public FilterReply getTurboFilterChainDecision(final Marker marker,
final Logger logger, final Level level, final String format,
final Object[] params, final Throwable t) {
if (cfai == null) {
return FilterReply.NEUTRAL;
}
return cfai.getTurboFilterChainDecision(marker, logger, level, format,
params, t);
}
final public FilterReply getTurboFilterChainDecision(final Marker marker,
final Logger logger, final Level level, final String format,
final Object param, final Throwable t) {
if (cfai == null) {
return FilterReply.NEUTRAL;
}
return cfai.getTurboFilterChainDecision(marker, logger, level, format,
new Object[]{param}, t);
}
final public FilterReply getTurboFilterChainDecision(final Marker marker,
final Logger logger, final Level level, final String format,
final Object param1, final Object param2, final Throwable t) {
if (cfai == null) {
return FilterReply.NEUTRAL;
}
return cfai.getTurboFilterChainDecision(marker, logger, level, format,
new Object[]{param1, param2}, t);
}
public TurboFilter getFirstTurboFilter() {
if (cfai == null) {
return null;
}
return cfai.getFirstTurboFilter();
}
public void addListener(ContextListener listener) {
contextListenerList.add(listener);
}
public void removeListener(ContextListener listener) {
contextListenerList.remove(listener);
}
private void fireOnReset() {
for (ContextListener listener : contextListenerList) {
listener.onReset(this);
}
}
private void fireOnStart() {
for (ContextListener listener : contextListenerList) {
listener.onStart(this);
}
}
public boolean isStarted() {
return started;
}
public void start() {
started = true;
fireOnStart();
}
public void stop() {
started = false;
}
}