Simple Logging Facade for Java (SLF4J)与Logback

SLF4J与Logback

日志门面

The Simple Logging Facade for Java (SLF4J) serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging, logback, log4j) allowing the end user to plug in the desired logging framework at deployment time.

Before you start using SLF4J, we highly recommend that you read the two-page SLF4J user manual.

Note that SLF4J-enabling your library implies the addition of only a single mandatory dependency, namely slf4j-api.jar. If no binding is found on the class path, then SLF4J will default to a no-operation implementation.

In case you wish to migrate your Java source files to SLF4J, consider our migrator tool which can migrate your project to use the SLF4J API in just a few minutes.

In case an externally-maintained component you depend on uses a logging API other than SLF4J, such as commons logging, log4j or java.util.logging, have a look at SLF4J's binary-support for legacy APIs.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}

1、找到org.slf4j.impl.StaticLoggerBinder implements LoggerFactoryBinder 使用了单例设计模式。

2、再获取Logger上下文,ch.qos.logback.classic.LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle。

3、再从Logger上下文中获取Logger 使用了容器单例设计模式。

/**
 * Logback: the reliable, generic, fast and flexible logging framework.
 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
 *
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation
 *
 *   or (per the licensee's choosing)
 *
 * under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation.
 */
package org.slf4j.impl;

import ch.qos.logback.core.status.StatusUtil;
import org.slf4j.ILoggerFactory;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.Util;
import org.slf4j.spi.LoggerFactoryBinder;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.util.ContextInitializer;
import ch.qos.logback.classic.util.ContextSelectorStaticBinder;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;

/**
 * 
 * The binding of {@link LoggerFactory} class with an actual instance of
 * {@link ILoggerFactory} is performed using information returned by this class.
 * 
 * @author Ceki G&uuml;lc&uuml;</a>
 */
public class StaticLoggerBinder implements LoggerFactoryBinder {

    /**
     * Declare the version of the SLF4J API this implementation is compiled
     * against. The value of this field is usually modified with each release.
     */
    // to avoid constant folding by the compiler, this field must *not* be final
    public static String REQUESTED_API_VERSION = "1.7.16"; // !final

    final static String NULL_CS_URL = CoreConstants.CODES_URL + "#null_CS";

    /**
     * The unique instance of this class.
     */
    private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();

    private static Object KEY = new Object();

    static {
        SINGLETON.init();
    }

    private boolean initialized = false;
    private LoggerContext defaultLoggerContext = new LoggerContext();
    private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder.getSingleton();

    private StaticLoggerBinder() {
        defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);
    }

    public static StaticLoggerBinder getSingleton() {
        return SINGLETON;
    }

    /**
     * Package access for testing purposes.
     */
    static void reset() {
        SINGLETON = new StaticLoggerBinder();
        SINGLETON.init();
    }

    /**
     * Package access for testing purposes.
     */
    void init() {
        try {
            try {
                new ContextInitializer(defaultLoggerContext).autoConfig();
            } catch (JoranException je) {
                Util.report("Failed to auto configure default logger context", je);
            }
            // logback-292
            if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
                StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
            }
            contextSelectorBinder.init(defaultLoggerContext, KEY);
            initialized = true;
        } catch (Exception t) { // see LOGBACK-1159
            Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t);
        }
    }

    public ILoggerFactory getLoggerFactory() {
        if (!initialized) {
            return defaultLoggerContext;
        }

        if (contextSelectorBinder.getContextSelector() == null) {
            throw new IllegalStateException("contextSelector cannot be null. See also " + NULL_CS_URL);
        }
        return contextSelectorBinder.getContextSelector().getLoggerContext();
    }

    public String getLoggerFactoryClassStr() {
        return contextSelectorBinder.getClass().getName();
    }

}
/**
 * Logback: the reliable, generic, fast and flexible logging framework.
 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
 *
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation
 *
 *   or (per the licensee's choosing)
 *
 * under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation.
 */
package ch.qos.logback.classic;

import static ch.qos.logback.core.CoreConstants.EVALUATOR_MAP;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

import org.slf4j.ILoggerFactory;
import org.slf4j.Marker;

import ch.qos.logback.classic.spi.LoggerComparator;
import ch.qos.logback.classic.spi.LoggerContextListener;
import ch.qos.logback.classic.spi.LoggerContextVO;
import ch.qos.logback.classic.spi.TurboFilterList;
import ch.qos.logback.classic.turbo.TurboFilter;
import ch.qos.logback.classic.util.LoggerNameUtil;
import ch.qos.logback.core.ContextBase;
import ch.qos.logback.core.boolex.EventEvaluator;
import ch.qos.logback.core.spi.FilterReply;
import ch.qos.logback.core.spi.LifeCycle;
import ch.qos.logback.core.status.StatusListener;
import ch.qos.logback.core.status.StatusManager;
import ch.qos.logback.core.status.WarnStatus;

/**
 * LoggerContext glues many of the logback-classic components together. In
 * principle, every logback-classic component instance is attached either
 * directly or indirectly 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, LifeCycle {

    /** Default setting of packaging data in stack traces */
    public static final boolean DEFAULT_PACKAGING_DATA = false;

    final Logger root;
    private int size;
    private int noAppenderWarning = 0;
    final private List<LoggerContextListener> loggerContextListenerList = new ArrayList<LoggerContextListener>();

    private Map<String, Logger> loggerCache;

    private LoggerContextVO loggerContextRemoteView;
    private final TurboFilterList turboFilterList = new TurboFilterList();
    private boolean packagingDataEnabled = DEFAULT_PACKAGING_DATA;

    private int maxCallerDataDepth = ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH;

    int resetCount = 0;
    private List<String> frameworkPackages;

    public LoggerContext() {
        super();
        this.loggerCache = new ConcurrentHashMap<String, Logger>();

        this.loggerContextRemoteView = new LoggerContextVO(this);
        this.root = new Logger(Logger.ROOT_LOGGER_NAME, null, this);
        this.root.setLevel(Level.DEBUG);
        loggerCache.put(Logger.ROOT_LOGGER_NAME, root);
        initEvaluatorMap();
        size = 1;
        this.frameworkPackages = new ArrayList<String>();
    }

    void initEvaluatorMap() {
        putObject(EVALUATOR_MAP, new HashMap<String, EventEvaluator<?>>());
    }

    /**
     * A new instance of LoggerContextRemoteView needs to be created each time the
     * name or propertyMap (including keys or values) changes.
     */
    private void updateLoggerContextVO() {
        loggerContextRemoteView = new LoggerContextVO(this);
    }

    @Override
    public void putProperty(String key, String val) {
        super.putProperty(key, val);
        updateLoggerContextVO();
    }

    @Override
    public void setName(String name) {
        super.setName(name);
        updateLoggerContextVO();
    }

    public final Logger getLogger(final Class<?> clazz) {
        return getLogger(clazz.getName());
    }

    @Override
    public final Logger getLogger(final String name) {

        if (name == null) {
            throw new IllegalArgumentException("name argument cannot be null");
        }

        // if we are asking for the root logger, then let us return it without
        // wasting time
        if (Logger.ROOT_LOGGER_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 = LoggerNameUtil.getSeparatorIndexOf(name, 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 void incSize() {
        size++;
    }

    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 WarnStatus("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 LoggerContextVO getLoggerContextRemoteView() {
        return loggerContextRemoteView;
    }

    public void setPackagingDataEnabled(boolean packagingDataEnabled) {
        this.packagingDataEnabled = packagingDataEnabled;
    }

    public boolean isPackagingDataEnabled() {
        return packagingDataEnabled;
    }

    /**
     * This method clears all internal properties, except internal status messages,
     * closes all appenders, removes any turboFilters, fires an OnReset event,
     * removes all status listeners, removes all context listeners
     * (except those which are reset resistant).
     * <p/>
     * As mentioned above, internal status messages survive resets.
     */
    @Override
    public void reset() {
        resetCount++;
        super.reset();
        initEvaluatorMap();
        initCollisionMaps();
        root.recursiveReset();
        resetTurboFilterList();
        cancelScheduledTasks();
        fireOnReset();
        resetListenersExceptResetResistant();
        resetStatusListeners();
    }

    private void cancelScheduledTasks() {
        for(ScheduledFuture<?> sf: scheduledFutures) {
            sf.cancel(false);
        }
        scheduledFutures.clear();
    }

    private void resetStatusListeners() {
        StatusManager sm = getStatusManager();
        for (StatusListener sl : sm.getCopyOfStatusListenerList()) {
            sm.remove(sl);
        }
    }

    public TurboFilterList getTurboFilterList() {
        return turboFilterList;
    }

    public void addTurboFilter(TurboFilter newFilter) {
        turboFilterList.add(newFilter);
    }

    /**
     * First processPriorToRemoval all registered turbo filters and then clear the registration
     * list.
     */
    public void resetTurboFilterList() {
        for (TurboFilter tf : turboFilterList) {
            tf.stop();
        }
        turboFilterList.clear();
    }

    final FilterReply getTurboFilterChainDecision_0_3OrMore(final Marker marker, final Logger logger, final Level level, final String format,
                    final Object[] params, final Throwable t) {
        if (turboFilterList.size() == 0) {
            return FilterReply.NEUTRAL;
        }
        return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, params, t);
    }

    final FilterReply getTurboFilterChainDecision_1(final Marker marker, final Logger logger, final Level level, final String format, final Object param,
                    final Throwable t) {
        if (turboFilterList.size() == 0) {
            return FilterReply.NEUTRAL;
        }
        return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, new Object[] { param }, t);
    }

    final FilterReply getTurboFilterChainDecision_2(final Marker marker, final Logger logger, final Level level, final String format, final Object param1,
                    final Object param2, final Throwable t) {
        if (turboFilterList.size() == 0) {
            return FilterReply.NEUTRAL;
        }
        return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, new Object[] { param1, param2 }, t);
    }

    // === start listeners ==============================================
    public void addListener(LoggerContextListener listener) {
        loggerContextListenerList.add(listener);
    }

    public void removeListener(LoggerContextListener listener) {
        loggerContextListenerList.remove(listener);
    }

    private void resetListenersExceptResetResistant() {
        List<LoggerContextListener> toRetain = new ArrayList<LoggerContextListener>();

        for (LoggerContextListener lcl : loggerContextListenerList) {
            if (lcl.isResetResistant()) {
                toRetain.add(lcl);
            }
        }
        loggerContextListenerList.retainAll(toRetain);
    }

    private void resetAllListeners() {
        loggerContextListenerList.clear();
    }

    public List<LoggerContextListener> getCopyOfListenerList() {
        return new ArrayList<LoggerContextListener>(loggerContextListenerList);
    }

    void fireOnLevelChange(Logger logger, Level level) {
        for (LoggerContextListener listener : loggerContextListenerList) {
            listener.onLevelChange(logger, level);
        }
    }

    private void fireOnReset() {
        for (LoggerContextListener listener : loggerContextListenerList) {
            listener.onReset(this);
        }
    }

    private void fireOnStart() {
        for (LoggerContextListener listener : loggerContextListenerList) {
            listener.onStart(this);
        }
    }

    private void fireOnStop() {
        for (LoggerContextListener listener : loggerContextListenerList) {
            listener.onStop(this);
        }
    }

    // === end listeners ==============================================

    public void start() {
        super.start();
        fireOnStart();
    }

    public void stop() {
        reset();
        fireOnStop();
        resetAllListeners();
        super.stop();
    }

    @Override
    public String toString() {
        return this.getClass().getName() + "[" + getName() + "]";
    }

    public int getMaxCallerDataDepth() {
        return maxCallerDataDepth;
    }

    public void setMaxCallerDataDepth(int maxCallerDataDepth) {
        this.maxCallerDataDepth = maxCallerDataDepth;
    }

    /**
     * List of packages considered part of the logging framework such that they are never considered
     * as callers of the logging framework. This list used to compute the caller for logging events.
     * <p/>
     * To designate package "com.foo" as well as all its subpackages as being part of the logging framework, simply add
     * "com.foo" to this list.
     *
     * @return list of framework packages
     */
    public List<String> getFrameworkPackages() {
        return frameworkPackages;
    }
}
/**
 * Logback: the reliable, generic, fast and flexible logging framework.
 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
 *
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation
 *
 *   or (per the licensee's choosing)
 *
 * under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation.
 */
package ch.qos.logback.classic.util;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Set;

import ch.qos.logback.classic.BasicConfigurator;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.gaffer.GafferUtil;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.classic.spi.Configurator;
import ch.qos.logback.core.LogbackException;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.status.ErrorStatus;
import ch.qos.logback.core.status.InfoStatus;
import ch.qos.logback.core.status.StatusManager;
import ch.qos.logback.core.status.WarnStatus;
import ch.qos.logback.core.util.Loader;
import ch.qos.logback.core.util.OptionHelper;
import ch.qos.logback.core.util.StatusListenerConfigHelper;

// contributors
// Ted Graham, Matt Fowles, see also http://jira.qos.ch/browse/LBCORE-32

/**
 * This class contains logback's logic for automatic configuration
 *
 * @author Ceki Gulcu
 */
public class ContextInitializer {

    final public static String GROOVY_AUTOCONFIG_FILE = "logback.groovy";
    final public static String AUTOCONFIG_FILE = "logback.xml";
    final public static String TEST_AUTOCONFIG_FILE = "logback-test.xml";
    final public static String CONFIG_FILE_PROPERTY = "logback.configurationFile";

    final LoggerContext loggerContext;

    public ContextInitializer(LoggerContext loggerContext) {
        this.loggerContext = loggerContext;
    }

    public void configureByResource(URL url) throws JoranException {
        if (url == null) {
            throw new IllegalArgumentException("URL argument cannot be null");
        }
        final String urlString = url.toString();
        if (urlString.endsWith("groovy")) {
            if (EnvUtil.isGroovyAvailable()) {
                // avoid directly referring to GafferConfigurator so as to avoid
                // loading groovy.lang.GroovyObject . See also http://jira.qos.ch/browse/LBCLASSIC-214
                GafferUtil.runGafferConfiguratorOn(loggerContext, this, url);
            } else {
                StatusManager sm = loggerContext.getStatusManager();
                sm.add(new ErrorStatus("Groovy classes are not available on the class path. ABORTING INITIALIZATION.", loggerContext));
            }
        } else if (urlString.endsWith("xml")) {
            JoranConfigurator configurator = new JoranConfigurator();
            configurator.setContext(loggerContext);
            configurator.doConfigure(url);
        } else {
            throw new LogbackException("Unexpected filename extension of file [" + url.toString() + "]. Should be either .groovy or .xml");
        }
    }

    void joranConfigureByResource(URL url) throws JoranException {
        JoranConfigurator configurator = new JoranConfigurator();
        configurator.setContext(loggerContext);
        configurator.doConfigure(url);
    }

    private URL findConfigFileURLFromSystemProperties(ClassLoader classLoader, boolean updateStatus) {
        String logbackConfigFile = OptionHelper.getSystemProperty(CONFIG_FILE_PROPERTY);
        if (logbackConfigFile != null) {
            URL result = null;
            try {
                result = new URL(logbackConfigFile);
                return result;
            } catch (MalformedURLException e) {
                // so, resource is not a URL:
                // attempt to get the resource from the class path
                result = Loader.getResource(logbackConfigFile, classLoader);
                if (result != null) {
                    return result;
                }
                File f = new File(logbackConfigFile);
                if (f.exists() && f.isFile()) {
                    try {
                        result = f.toURI().toURL();
                        return result;
                    } catch (MalformedURLException e1) {
                    }
                }
            } finally {
                if (updateStatus) {
                    statusOnResourceSearch(logbackConfigFile, classLoader, result);
                }
            }
        }
        return null;
    }

    public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {
        ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
        URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
        if (url != null) {
            return url;
        }

        url = getResource(TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus);
        if (url != null) {
            return url;
        }

        url = getResource(GROOVY_AUTOCONFIG_FILE, myClassLoader, updateStatus);
        if (url != null) {
            return url;
        }

        return getResource(AUTOCONFIG_FILE, myClassLoader, updateStatus);
    }

    private URL getResource(String filename, ClassLoader myClassLoader, boolean updateStatus) {
        URL url = Loader.getResource(filename, myClassLoader);
        if (updateStatus) {
            statusOnResourceSearch(filename, myClassLoader, url);
        }
        return url;
    }

    public void autoConfig() throws JoranException {
        StatusListenerConfigHelper.installIfAsked(loggerContext);
        URL url = findURLOfDefaultConfigurationFile(true);
        if (url != null) {
            configureByResource(url);
        } else {
            Configurator c = EnvUtil.loadFromServiceLoader(Configurator.class);
            if (c != null) {
                try {
                    c.setContext(loggerContext);
                    c.configure(loggerContext);
                } catch (Exception e) {
                    throw new LogbackException(String.format("Failed to initialize Configurator: %s using ServiceLoader", c != null ? c.getClass()
                                    .getCanonicalName() : "null"), e);
                }
            } else {
                BasicConfigurator basicConfigurator = new BasicConfigurator();
                basicConfigurator.setContext(loggerContext);
                basicConfigurator.configure(loggerContext);
            }
        }
    }

    private void statusOnResourceSearch(String resourceName, ClassLoader classLoader, URL url) {
        StatusManager sm = loggerContext.getStatusManager();
        if (url == null) {
            sm.add(new InfoStatus("Could NOT find resource [" + resourceName + "]", loggerContext));
        } else {
            sm.add(new InfoStatus("Found resource [" + resourceName + "] at [" + url.toString() + "]", loggerContext));
            multiplicityWarning(resourceName, classLoader);
        }
    }

    private void multiplicityWarning(String resourceName, ClassLoader classLoader) {
        Set<URL> urlSet = null;
        StatusManager sm = loggerContext.getStatusManager();
        try {
            urlSet = Loader.getResources(resourceName, classLoader);
        } catch (IOException e) {
            sm.add(new ErrorStatus("Failed to get url list for resource [" + resourceName + "]", loggerContext, e));
        }
        if (urlSet != null && urlSet.size() > 1) {
            sm.add(new WarnStatus("Resource [" + resourceName + "] occurs multiple times on the classpath.", loggerContext));
            for (URL url : urlSet) {
                sm.add(new WarnStatus("Resource [" + resourceName + "] occurs at [" + url.toString() + "]", loggerContext));
            }
        }
    }
}
/**
 * Logback: the reliable, generic, fast and flexible logging framework.
 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
 *
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation
 *
 *   or (per the licensee's choosing)
 *
 * under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation.
 */
package ch.qos.logback.classic;

import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.spi.LocationAwareLogger;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.classic.util.LoggerNameUtil;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.spi.AppenderAttachable;
import ch.qos.logback.core.spi.AppenderAttachableImpl;
import ch.qos.logback.core.spi.FilterReply;

public final class Logger implements org.slf4j.Logger, LocationAwareLogger, AppenderAttachable<ILoggingEvent>, Serializable {

    private static final long serialVersionUID = 5454405123156820674L; // 8745934908040027998L;

    /**
     * The fully qualified name of this class. Used in gathering caller
     * information.
     */
    public static final String FQCN = ch.qos.logback.classic.Logger.class.getName();

    /**
     * The name of this logger
     */
    private String name;

    // The assigned levelInt of this logger. Can be null.
    transient private Level level;

    // The effective levelInt is the assigned levelInt and if null, a levelInt is
    // inherited form a parent.
    transient private int effectiveLevelInt;

    /**
     * The parent of this category. All categories have at least one ancestor
     * which is the root category.
     */
    transient private Logger parent;

    /**
     * The children of this logger. A logger may have zero or more children.
     */
    transient private List<Logger> childrenList;

    /**
     * It is assumed that once the 'aai' variable is set to a non-null value, it
     * will never be reset to null. it is further assumed that only place where
     * the 'aai'ariable is set is within the addAppender method. This method is
     * synchronized on 'this' (Logger) protecting against simultaneous
     * re-configuration of this logger (a very unlikely scenario).
     * 
     * <p>
     * It is further assumed that the AppenderAttachableImpl is responsible for
     * its internal synchronization and thread safety. Thus, we can get away with
     * *not* synchronizing on the 'aai' (check null/ read) because
     * <p>
     * 1) the 'aai' variable is immutable once set to non-null
     * <p>
     * 2) 'aai' is getAndSet only within addAppender which is synchronized
     * <p>
     * 3) all the other methods check whether 'aai' is null
     * <p>
     * 4) AppenderAttachableImpl is thread safe
     */
    transient private AppenderAttachableImpl<ILoggingEvent> aai;
    /**
     * Additivity is set to true by default, that is children inherit the
     * appenders of their ancestors by default. If this variable is set to
     * <code>false</code> then the appenders located in the ancestors of this
     * logger will not be used. However, the children of this logger will inherit
     * its appenders, unless the children have their additivity flag set to
     * <code>false</code> too. See the user manual for more details.
     */
    transient private boolean additive = true;

    final transient LoggerContext loggerContext;

    Logger(String name, Logger parent, LoggerContext loggerContext) {
        this.name = name;
        this.parent = parent;
        this.loggerContext = loggerContext;
    }

    public Level getEffectiveLevel() {
        return Level.toLevel(effectiveLevelInt);
    }

    int getEffectiveLevelInt() {
        return effectiveLevelInt;
    }

    public Level getLevel() {
        return level;
    }

    public String getName() {
        return name;
    }

    private boolean isRootLogger() {
        // only the root logger has a null parent
        return parent == null;
    }

    Logger getChildByName(final String childName) {
        if (childrenList == null) {
            return null;
        } else {
            int len = this.childrenList.size();
            for (int i = 0; i < len; i++) {
                final Logger childLogger_i = (Logger) childrenList.get(i);
                final String childName_i = childLogger_i.getName();

                if (childName.equals(childName_i)) {
                    return childLogger_i;
                }
            }
            // no child found
            return null;
        }
    }

    public synchronized void setLevel(Level newLevel) {
        if (level == newLevel) {
            // nothing to do;
            return;
        }
        if (newLevel == null && isRootLogger()) {
            throw new IllegalArgumentException("The level of the root logger cannot be set to null");
        }

        level = newLevel;
        if (newLevel == null) {
            effectiveLevelInt = parent.effectiveLevelInt;
            newLevel = parent.getEffectiveLevel();
        } else {
            effectiveLevelInt = newLevel.levelInt;
        }

        if (childrenList != null) {
            int len = childrenList.size();
            for (int i = 0; i < len; i++) {
                Logger child = (Logger) childrenList.get(i);
                // tell child to handle parent levelInt change
                child.handleParentLevelChange(effectiveLevelInt);
            }
        }
        // inform listeners
        loggerContext.fireOnLevelChange(this, newLevel);
    }

    /**
     * This method is invoked by parent logger to let this logger know that the
     * prent's levelInt changed.
     * 
     * @param newParentLevelInt
     */
    private synchronized void handleParentLevelChange(int newParentLevelInt) {
        // changes in the parent levelInt affect children only if their levelInt is
        // null
        if (level == null) {
            effectiveLevelInt = newParentLevelInt;

            // propagate the parent levelInt change to this logger's children
            if (childrenList != null) {
                int len = childrenList.size();
                for (int i = 0; i < len; i++) {
                    Logger child = (Logger) childrenList.get(i);
                    child.handleParentLevelChange(newParentLevelInt);
                }
            }
        }
    }

    /**
     * Remove all previously added appenders from this logger instance.
     * <p/>
     * This is useful when re-reading configuration information.
     */
    public void detachAndStopAllAppenders() {
        if (aai != null) {
            aai.detachAndStopAllAppenders();
        }
    }

    public boolean detachAppender(String name) {
        if (aai == null) {
            return false;
        }
        return aai.detachAppender(name);
    }

    // this method MUST be synchronized. See comments on 'aai' field for further
    // details.
    public synchronized void addAppender(Appender<ILoggingEvent> newAppender) {
        if (aai == null) {
            aai = new AppenderAttachableImpl<ILoggingEvent>();
        }
        aai.addAppender(newAppender);
    }

    public boolean isAttached(Appender<ILoggingEvent> appender) {
        if (aai == null) {
            return false;
        }
        return aai.isAttached(appender);
    }

    @SuppressWarnings("unchecked")
    public Iterator<Appender<ILoggingEvent>> iteratorForAppenders() {
        if (aai == null) {
            return Collections.EMPTY_LIST.iterator();
        }
        return aai.iteratorForAppenders();
    }

    public Appender<ILoggingEvent> getAppender(String name) {
        if (aai == null) {
            return null;
        }
        return aai.getAppender(name);
    }

    /**
     * Invoke all the appenders of this logger.
     * 
     * @param event
     *          The event to log
     */
    public void callAppenders(ILoggingEvent event) {
        int writes = 0;
        for (Logger l = this; l != null; l = l.parent) {
            writes += l.appendLoopOnAppenders(event);
            if (!l.additive) {
                break;
            }
        }
        // No appenders in hierarchy
        if (writes == 0) {
            loggerContext.noAppenderDefinedWarning(this);
        }
    }

    private int appendLoopOnAppenders(ILoggingEvent event) {
        if (aai != null) {
            return aai.appendLoopOnAppenders(event);
        } else {
            return 0;
        }
    }

    /**
     * Remove the appender passed as parameter form the list of appenders.
     */
    public boolean detachAppender(Appender<ILoggingEvent> appender) {
        if (aai == null) {
            return false;
        }
        return aai.detachAppender(appender);
    }

    /**
     * Create a child of this logger by suffix, that is, the part of the name
     * extending this logger. For example, if this logger is named "x.y" and the
     * lastPart is "z", then the created child logger will be named "x.y.z".
     * 
     * <p>
     * IMPORTANT: Calls to this method must be within a synchronized block on this
     * logger.
     * 
     * @param lastPart
     *          the suffix (i.e. last part) of the child logger name. This
     *          parameter may not include dots, i.e. the logger separator
     *          character.
     * @return
     */
    Logger createChildByLastNamePart(final String lastPart) {
        int i_index = LoggerNameUtil.getFirstSeparatorIndexOf(lastPart);
        if (i_index != -1) {
            throw new IllegalArgumentException("Child name [" + lastPart + " passed as parameter, may not include [" + CoreConstants.DOT + "]");
        }

        if (childrenList == null) {
            childrenList = new CopyOnWriteArrayList<Logger>();
        }
        Logger childLogger;
        if (this.isRootLogger()) {
            childLogger = new Logger(lastPart, this, this.loggerContext);
        } else {
            childLogger = new Logger(name + CoreConstants.DOT + lastPart, this, this.loggerContext);
        }
        childrenList.add(childLogger);
        childLogger.effectiveLevelInt = this.effectiveLevelInt;
        return childLogger;
    }

    private void localLevelReset() {
        effectiveLevelInt = Level.DEBUG_INT;
        if (isRootLogger()) {
            level = Level.DEBUG;
        } else {
            level = null;
        }
    }

    void recursiveReset() {
        detachAndStopAllAppenders();
        localLevelReset();
        additive = true;
        if (childrenList == null) {
            return;
        }
        for (Logger childLogger : childrenList) {
            childLogger.recursiveReset();
        }
    }

    /**
     * The default size of child list arrays. The JDK 1.5 default is 10. We use a
     * smaller value to save a little space.
     */

    Logger createChildByName(final String childName) {
        int i_index = LoggerNameUtil.getSeparatorIndexOf(childName, this.name.length() + 1);
        if (i_index != -1) {
            throw new IllegalArgumentException("For logger [" + this.name + "] child name [" + childName
                            + " passed as parameter, may not include '.' after index" + (this.name.length() + 1));
        }

        if (childrenList == null) {
            childrenList = new CopyOnWriteArrayList<Logger>();
        }
        Logger childLogger;
        childLogger = new Logger(childName, this, this.loggerContext);
        childrenList.add(childLogger);
        childLogger.effectiveLevelInt = this.effectiveLevelInt;
        return childLogger;
    }

    /**
     * The next methods are not merged into one because of the time we gain by not
     * creating a new Object[] with the params. This reduces the cost of not
     * logging by about 20 nanoseconds.
     */

    private void filterAndLog_0_Or3Plus(final String localFQCN, final Marker marker, final Level level, final String msg, final Object[] params,
                    final Throwable t) {

        final FilterReply decision = loggerContext.getTurboFilterChainDecision_0_3OrMore(marker, this, level, msg, params, t);

        if (decision == FilterReply.NEUTRAL) {
            if (effectiveLevelInt > level.levelInt) {
                return;
            }
        } else if (decision == FilterReply.DENY) {
            return;
        }

        buildLoggingEventAndAppend(localFQCN, marker, level, msg, params, t);
    }

    private void filterAndLog_1(final String localFQCN, final Marker marker, final Level level, final String msg, final Object param, final Throwable t) {

        final FilterReply decision = loggerContext.getTurboFilterChainDecision_1(marker, this, level, msg, param, t);

        if (decision == FilterReply.NEUTRAL) {
            if (effectiveLevelInt > level.levelInt) {
                return;
            }
        } else if (decision == FilterReply.DENY) {
            return;
        }

        buildLoggingEventAndAppend(localFQCN, marker, level, msg, new Object[] { param }, t);
    }

    private void filterAndLog_2(final String localFQCN, final Marker marker, final Level level, final String msg, final Object param1, final Object param2,
                    final Throwable t) {

        final FilterReply decision = loggerContext.getTurboFilterChainDecision_2(marker, this, level, msg, param1, param2, t);

        if (decision == FilterReply.NEUTRAL) {
            if (effectiveLevelInt > level.levelInt) {
                return;
            }
        } else if (decision == FilterReply.DENY) {
            return;
        }

        buildLoggingEventAndAppend(localFQCN, marker, level, msg, new Object[] { param1, param2 }, t);
    }

    private void buildLoggingEventAndAppend(final String localFQCN, final Marker marker, final Level level, final String msg, final Object[] params,
                    final Throwable t) {
        LoggingEvent le = new LoggingEvent(localFQCN, this, level, msg, t, params);
        le.setMarker(marker);
        callAppenders(le);
    }

    public void trace(String msg) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.TRACE, msg, null, null);
    }

    public void trace(String format, Object arg) {
        filterAndLog_1(FQCN, null, Level.TRACE, format, arg, null);
    }

    public void trace(String format, Object arg1, Object arg2) {
        filterAndLog_2(FQCN, null, Level.TRACE, format, arg1, arg2, null);
    }

    public void trace(String format, Object... argArray) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.TRACE, format, argArray, null);
    }

    public void trace(String msg, Throwable t) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.TRACE, msg, null, t);
    }

    public void trace(Marker marker, String msg) {
        filterAndLog_0_Or3Plus(FQCN, marker, Level.TRACE, msg, null, null);
    }

    public void trace(Marker marker, String format, Object arg) {
        filterAndLog_1(FQCN, marker, Level.TRACE, format, arg, null);
    }

    public void trace(Marker marker, String format, Object arg1, Object arg2) {
        filterAndLog_2(FQCN, marker, Level.TRACE, format, arg1, arg2, null);
    }

    public void trace(Marker marker, String format, Object... argArray) {
        filterAndLog_0_Or3Plus(FQCN, marker, Level.TRACE, format, argArray, null);
    }

    public void trace(Marker marker, String msg, Throwable t) {
        filterAndLog_0_Or3Plus(FQCN, marker, Level.TRACE, msg, null, t);
    }

    public boolean isDebugEnabled() {
        return isDebugEnabled(null);
    }

    public boolean isDebugEnabled(Marker marker) {
        final FilterReply decision = callTurboFilters(marker, Level.DEBUG);
        if (decision == FilterReply.NEUTRAL) {
            return effectiveLevelInt <= Level.DEBUG_INT;
        } else if (decision == FilterReply.DENY) {
            return false;
        } else if (decision == FilterReply.ACCEPT) {
            return true;
        } else {
            throw new IllegalStateException("Unknown FilterReply value: " + decision);
        }
    }

    public void debug(String msg) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.DEBUG, msg, null, null);
    }

    public void debug(String format, Object arg) {
        filterAndLog_1(FQCN, null, Level.DEBUG, format, arg, null);
    }

    public void debug(String format, Object arg1, Object arg2) {
        filterAndLog_2(FQCN, null, Level.DEBUG, format, arg1, arg2, null);
    }

    public void debug(String format, Object... argArray) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.DEBUG, format, argArray, null);
    }

    public void debug(String msg, Throwable t) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.DEBUG, msg, null, t);
    }

    public void debug(Marker marker, String msg) {
        filterAndLog_0_Or3Plus(FQCN, marker, Level.DEBUG, msg, null, null);
    }

    public void debug(Marker marker, String format, Object arg) {
        filterAndLog_1(FQCN, marker, Level.DEBUG, format, arg, null);
    }

    public void debug(Marker marker, String format, Object arg1, Object arg2) {
        filterAndLog_2(FQCN, marker, Level.DEBUG, format, arg1, arg2, null);
    }

    public void debug(Marker marker, String format, Object... argArray) {
        filterAndLog_0_Or3Plus(FQCN, marker, Level.DEBUG, format, argArray, null);
    }

    public void debug(Marker marker, String msg, Throwable t) {
        filterAndLog_0_Or3Plus(FQCN, marker, Level.DEBUG, msg, null, t);
    }

    public void error(String msg) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.ERROR, msg, null, null);
    }

    public void error(String format, Object arg) {
        filterAndLog_1(FQCN, null, Level.ERROR, format, arg, null);
    }

    public void error(String format, Object arg1, Object arg2) {
        filterAndLog_2(FQCN, null, Level.ERROR, format, arg1, arg2, null);
    }

    public void error(String format, Object... argArray) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.ERROR, format, argArray, null);
    }

    public void error(String msg, Throwable t) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.ERROR, msg, null, t);
    }

    public void error(Marker marker, String msg) {
        filterAndLog_0_Or3Plus(FQCN, marker, Level.ERROR, msg, null, null);
    }

    public void error(Marker marker, String format, Object arg) {
        filterAndLog_1(FQCN, marker, Level.ERROR, format, arg, null);
    }

    public void error(Marker marker, String format, Object arg1, Object arg2) {
        filterAndLog_2(FQCN, marker, Level.ERROR, format, arg1, arg2, null);
    }

    public void error(Marker marker, String format, Object... argArray) {
        filterAndLog_0_Or3Plus(FQCN, marker, Level.ERROR, format, argArray, null);
    }

    public void error(Marker marker, String msg, Throwable t) {
        filterAndLog_0_Or3Plus(FQCN, marker, Level.ERROR, msg, null, t);
    }

    public boolean isInfoEnabled() {
        return isInfoEnabled(null);
    }

    public boolean isInfoEnabled(Marker marker) {
        FilterReply decision = callTurboFilters(marker, Level.INFO);
        if (decision == FilterReply.NEUTRAL) {
            return effectiveLevelInt <= Level.INFO_INT;
        } else if (decision == FilterReply.DENY) {
            return false;
        } else if (decision == FilterReply.ACCEPT) {
            return true;
        } else {
            throw new IllegalStateException("Unknown FilterReply value: " + decision);
        }
    }

    public void info(String msg) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.INFO, msg, null, null);
    }

    public void info(String format, Object arg) {
        filterAndLog_1(FQCN, null, Level.INFO, format, arg, null);
    }

    public void info(String format, Object arg1, Object arg2) {
        filterAndLog_2(FQCN, null, Level.INFO, format, arg1, arg2, null);
    }

    public void info(String format, Object... argArray) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.INFO, format, argArray, null);
    }

    public void info(String msg, Throwable t) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.INFO, msg, null, t);
    }

    public void info(Marker marker, String msg) {
        filterAndLog_0_Or3Plus(FQCN, marker, Level.INFO, msg, null, null);
    }

    public void info(Marker marker, String format, Object arg) {
        filterAndLog_1(FQCN, marker, Level.INFO, format, arg, null);
    }

    public void info(Marker marker, String format, Object arg1, Object arg2) {
        filterAndLog_2(FQCN, marker, Level.INFO, format, arg1, arg2, null);
    }

    public void info(Marker marker, String format, Object... argArray) {
        filterAndLog_0_Or3Plus(FQCN, marker, Level.INFO, format, argArray, null);
    }

    public void info(Marker marker, String msg, Throwable t) {
        filterAndLog_0_Or3Plus(FQCN, marker, Level.INFO, msg, null, t);
    }

    public boolean isTraceEnabled() {
        return isTraceEnabled(null);
    }

    public boolean isTraceEnabled(Marker marker) {
        final FilterReply decision = callTurboFilters(marker, Level.TRACE);
        if (decision == FilterReply.NEUTRAL) {
            return effectiveLevelInt <= Level.TRACE_INT;
        } else if (decision == FilterReply.DENY) {
            return false;
        } else if (decision == FilterReply.ACCEPT) {
            return true;
        } else {
            throw new IllegalStateException("Unknown FilterReply value: " + decision);
        }
    }

    public boolean isErrorEnabled() {
        return isErrorEnabled(null);
    }

    public boolean isErrorEnabled(Marker marker) {
        FilterReply decision = callTurboFilters(marker, Level.ERROR);
        if (decision == FilterReply.NEUTRAL) {
            return effectiveLevelInt <= Level.ERROR_INT;
        } else if (decision == FilterReply.DENY) {
            return false;
        } else if (decision == FilterReply.ACCEPT) {
            return true;
        } else {
            throw new IllegalStateException("Unknown FilterReply value: " + decision);
        }
    }

    public boolean isWarnEnabled() {
        return isWarnEnabled(null);
    }

    public boolean isWarnEnabled(Marker marker) {
        FilterReply decision = callTurboFilters(marker, Level.WARN);
        if (decision == FilterReply.NEUTRAL) {
            return effectiveLevelInt <= Level.WARN_INT;
        } else if (decision == FilterReply.DENY) {
            return false;
        } else if (decision == FilterReply.ACCEPT) {
            return true;
        } else {
            throw new IllegalStateException("Unknown FilterReply value: " + decision);
        }

    }

    public boolean isEnabledFor(Marker marker, Level level) {
        FilterReply decision = callTurboFilters(marker, level);
        if (decision == FilterReply.NEUTRAL) {
            return effectiveLevelInt <= level.levelInt;
        } else if (decision == FilterReply.DENY) {
            return false;
        } else if (decision == FilterReply.ACCEPT) {
            return true;
        } else {
            throw new IllegalStateException("Unknown FilterReply value: " + decision);
        }
    }

    public boolean isEnabledFor(Level level) {
        return isEnabledFor(null, level);
    }

    public void warn(String msg) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.WARN, msg, null, null);
    }

    public void warn(String msg, Throwable t) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.WARN, msg, null, t);
    }

    public void warn(String format, Object arg) {
        filterAndLog_1(FQCN, null, Level.WARN, format, arg, null);
    }

    public void warn(String format, Object arg1, Object arg2) {
        filterAndLog_2(FQCN, null, Level.WARN, format, arg1, arg2, null);
    }

    public void warn(String format, Object... argArray) {
        filterAndLog_0_Or3Plus(FQCN, null, Level.WARN, format, argArray, null);
    }

    public void warn(Marker marker, String msg) {
        filterAndLog_0_Or3Plus(FQCN, marker, Level.WARN, msg, null, null);
    }

    public void warn(Marker marker, String format, Object arg) {
        filterAndLog_1(FQCN, marker, Level.WARN, format, arg, null);
    }

    public void warn(Marker marker, String format, Object... argArray) {
        filterAndLog_0_Or3Plus(FQCN, marker, Level.WARN, format, argArray, null);
    }

    public void warn(Marker marker, String format, Object arg1, Object arg2) {
        filterAndLog_2(FQCN, marker, Level.WARN, format, arg1, arg2, null);
    }

    public void warn(Marker marker, String msg, Throwable t) {
        filterAndLog_0_Or3Plus(FQCN, marker, Level.WARN, msg, null, t);
    }

    public boolean isAdditive() {
        return additive;
    }

    public void setAdditive(boolean additive) {
        this.additive = additive;
    }

    public String toString() {
        return "Logger[" + name + "]";
    }

    /**
     * Method that calls the attached TurboFilter objects based on the logger and
     * the level.
     * 
     * It is used by isYYYEnabled() methods.
     * 
     * It returns the typical FilterReply values: ACCEPT, NEUTRAL or DENY.
     * 
     * @param level
     * @return the reply given by the TurboFilters
     */
    private FilterReply callTurboFilters(Marker marker, Level level) {
        return loggerContext.getTurboFilterChainDecision_0_3OrMore(marker, this, level, null, null, null);
    }

    /**
     * Return the context for this logger.
     * 
     * @return the context
     */
    public LoggerContext getLoggerContext() {
        return loggerContext;
    }

    public void log(Marker marker, String fqcn, int levelInt, String message, Object[] argArray, Throwable t) {
        Level level = Level.fromLocationAwareLoggerInteger(levelInt);
        filterAndLog_0_Or3Plus(fqcn, marker, level, message, argArray, t);
    }

    /**
     * Support SLF4J interception during initialization as introduced in SLF4J version 1.7.15
     * @since 1.1.4 
     * @param slf4jEvent
     */
    public void log(org.slf4j.event.LoggingEvent slf4jEvent) {
        Level level = Level.fromLocationAwareLoggerInteger(slf4jEvent.getLevel().toInt());
        filterAndLog_0_Or3Plus(FQCN, slf4jEvent.getMarker(), level, slf4jEvent.getMessage(), slf4jEvent.getArgumentArray(), slf4jEvent.getThrowable());
    }

    /**
     * After serialization, the logger instance does not know its LoggerContext.
     * The best we can do here, is to return a logger with the same name
     * returned by org.slf4j.LoggerFactory.
     * 
     * @return Logger instance with the same name
     * @throws ObjectStreamException
     */
    protected Object readResolve() throws ObjectStreamException {
        return LoggerFactory.getLogger(getName());
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值