FreeMarker源码分析(三)

2021SC@SDUSC

目录

Log包

SLF4JLoggerFactory.java

源码展示

代码分析

关于slf4j

Logger.java

源码展示

源码分析


Log包

本篇主要来分析LoggerFactory的余下代码和Logger代码

SLF4JLoggerFactory.java

源码展示

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package freemarker.log;

import org.slf4j.spi.LocationAwareLogger;

/**
 * @deprecated Don't use it, meant to be internal.
 */
// 2.4: Remove
@Deprecated
public class SLF4JLoggerFactory implements LoggerFactory {

    @Override
    public Logger getLogger(String category) {
            org.slf4j.Logger slf4jLogger = org.slf4j.LoggerFactory.getLogger(category);
            if (slf4jLogger instanceof LocationAwareLogger) {
                    return new LocationAwareSLF4JLogger((LocationAwareLogger) slf4jLogger);
            } else {
                    return new LocationUnawareSLF4JLogger(slf4jLogger);
            }
    }

    /**
     * Logger where the log entry issuer (class, method) will be correctly
     * shown to be the caller of <tt>LocationAwareSLF4JLogger</tt> methods.
     */
    private static final class LocationAwareSLF4JLogger extends Logger {
            
            private static final String ADAPTER_FQCN
                            = LocationAwareSLF4JLogger.class.getName();
            
            private final LocationAwareLogger logger;
            
            LocationAwareSLF4JLogger(LocationAwareLogger logger) {
                    this.logger = logger;
            }

            @Override
            public void debug(String message) {
                    debug(message, null);
            }

            @Override
            public void debug(String message, Throwable t) {
                    logger.log(null, ADAPTER_FQCN,
                                    LocationAwareLogger.DEBUG_INT, message, null, t);
            }

            @Override
            public void info(String message) {
                    info(message, null);
            }

            @Override
            public void info(String message, Throwable t) {
                    logger.log(null, ADAPTER_FQCN,
                                    LocationAwareLogger.INFO_INT, message, null, t);
            }

            @Override
            public void warn(String message) {
                    warn(message, null);
            }

            @Override
            public void warn(String message, Throwable t) {
                    logger.log(null, ADAPTER_FQCN,
                                    LocationAwareLogger.WARN_INT, message, null, t);
            }

            @Override
            public void error(String message) {
                    error(message, null);
            }

            @Override
            public void error(String message, Throwable t) {
                    logger.log(null, ADAPTER_FQCN,
                                    LocationAwareLogger.ERROR_INT, message, null, t);
            }

            @Override
            public boolean isDebugEnabled() {
                    return logger.isDebugEnabled();
            }

            @Override
            public boolean isInfoEnabled() {
                    return logger.isInfoEnabled();
            }

            @Override
            public boolean isWarnEnabled() {
                    return logger.isWarnEnabled();
            }

            @Override
            public boolean isErrorEnabled() {
                    return logger.isErrorEnabled();
            }

            @Override
            public boolean isFatalEnabled() {
                    return logger.isErrorEnabled();
            }
            
    }
    
    /**
     * Logger where the log entry issuer (class, method) will be incorrectly
     * shown to be a method of this class.
     */
    private static class LocationUnawareSLF4JLogger extends Logger {
            
            private final org.slf4j.Logger logger;

            LocationUnawareSLF4JLogger(org.slf4j.Logger logger) {
                    this.logger = logger;
            }

            @Override
            public void debug(String message) {
                    logger.debug(message);
            }

            @Override
            public void debug(String message, Throwable t) {
                    logger.debug(message, t);
            }

            @Override
            public void info(String message) {
                    logger.info(message);
            }

            @Override
            public void info(String message, Throwable t) {
                    logger.info(message, t);
            }

            @Override
            public void warn(String message) {
                    logger.warn(message);
            }

            @Override
            public void warn(String message, Throwable t) {
                    logger.warn(message, t);
            }

            @Override
            public void error(String message) {
                    logger.error(message);
            }

            @Override
            public void error(String message, Throwable t) {
                    logger.error(message, t);
            }

            @Override
            public boolean isDebugEnabled() {
                    return logger.isDebugEnabled();
            }

            @Override
            public boolean isInfoEnabled() {
                    return logger.isInfoEnabled();
            }

            @Override
            public boolean isWarnEnabled() {
                    return logger.isWarnEnabled();
            }

            @Override
            public boolean isErrorEnabled() {
                    return logger.isErrorEnabled();
            }

            @Override
            public boolean isFatalEnabled() {
                    return logger.isErrorEnabled();
            }
            
    }
    
}

代码分析

获取Logger的一个对象,重新定义父类的方法,申明 logger 变量

  public Logger getLogger(String category) {
            org.slf4j.Logger slf4jLogger = org.slf4j.LoggerFactory.getLogger(category);
            if (slf4jLogger instanceof LocationAwareLogger) {
                    return new LocationAwareSLF4JLogger((LocationAwareLogger) slf4jLogger);
            } else {
                    return new LocationUnawareSLF4JLogger(slf4jLogger);
            }
    }

关于slf4j

经过查阅资料分析后得知:

SLF4J(Simple logging Facade for Java)不是一个真正的日志实现,而是一个抽象层( abstraction layer),它允许你在后台使用任意一个日志类库。

SLF4J使你的代码独立于任意一个特定的日志API,编写供内外部都可以使用的API或者通用类库,那么你真不会希望使用你类库的客户端必须使用你选择的日志类库

SLF4J的占位符就比这个好用得多。所以, 一般应用使用日志框架都是基于slf4j的;

  1. 创建Logger对象: slf4j.Logger LOG= slf4j.LoggerFactory.getLogger(className);
  2. 一般使用接口: LOG.info(msg), LOG.debug(msg), LOG.warn(msg)...
  3. 有异常追踪的: LOG.info(msg,ex), LOG.debug(msg,ex), LOG.warn(msg,ex)...

Logger.java

源码展示



package freemarker.log;

import java.security.AccessControlException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Map;

/**
 * Delegates logger creation to an actual logging library. By default it looks for logger libraries in this order (in
 * FreeMarker 2.3.x): Log4J, Avalon LogKit, JUL (i.e., <tt>java.util.logging</tt>). Prior to FreeMarker 2.4, SLF4J and
 * Apache Commons Logging aren't searched automatically due to backward compatibility constraints. But if you have
 * {@code log4j-over-slf4j} properly installed (means, you have no real Log4j in your class path, and SLF4J has a
 * backing implementation like {@code logback-classic}), then FreeMarker will use SLF4J directly instead of Log4j (since
 * FreeMarker 2.3.22).
 * 
 * <p>
 * If the auto detection sequence describet above doesn't give you the result that you want, see
 * {@link #SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY}.
 */
public abstract class Logger {

    /**
     * The {@value #SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY} system property is used to select a logger library explicitly,
     * rather than relying on automatic selection. You meant to set this system property where the JVM is started, like
     * {@code java ... -Dorg.freemarker.loggerLibrary=SLF4j}. Setting it from Java code isn't reliable, also it can
     * cause confusion if you override the value set by others with {@code -D}.
     * 
     * <p>
     * If the requested logging library is not available, an error will be printed to the stderr, then logging will be
     * disabled.
     * 
     * <p>
     * The supported values are:
     * <ul>
     * <li>{@value #LIBRARY_NAME_SLF4J}: Use SLF4J (recommended)</li>
     * <li>{@value #LIBRARY_NAME_COMMONS_LOGGING}: Use Apache Commons Logging.</li>
     * <li>{@value #LIBRARY_NAME_LOG4J}: Use Log4j.</li>
     * <li>{@value #LIBRARY_NAME_JUL}: Use {@code java.util.logging}.</li>
     * <li>{@value #LIBRARY_NAME_AVALON}: Use Avalon LogKit (deprecated)</li>
     * <li>{@value #LIBRARY_NAME_AUTO}: Use automatic selection (default behavior). See {@link Logger} class
     * documentation for more.</li>
     * <li>{@value #LIBRARY_NAME_NONE}: Don't log.</li>
     * </ul>
     * 
     * @since 2.3.22
     */
    public static final String SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY = "org.freemarker.loggerLibrary";

    /**
     * Enum value used for {@link #selectLoggerLibrary(int)}; indicates auto-selection as described in the class
     * documentation of {@link #Logger()}.
     */
    public static final int LIBRARY_AUTO = -1;
    private static final int MIN_LIBRARY_ENUM = LIBRARY_AUTO;

    /**
     * {@value #SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY} property value; indicates auto-selection as described in the class
     * documentation of {@link #Logger()}. @since 2.3.22
     */
    public static final String LIBRARY_NAME_AUTO = "auto";

    /**
     * Enum value used for {@link #selectLoggerLibrary(int)}; indicates that no logging should occur.
     */
    public static final int LIBRARY_NONE = 0;

    /**
     * {@value #SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY} property value; indicates that no logging should occur. @since
     * 2.3.22
     */
    public static final String LIBRARY_NAME_NONE = "none";

    /**
     * Enum value used for {@link #selectLoggerLibrary(int)}; indicates that {@code java.util.logging} should be used.
     */
    public static final int LIBRARY_JAVA = 1;

    /**
     * {@value #SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY} property value; indicates that {@code java.util.logging} should be
     * used. @since 2.3.22
     */
    public static final String LIBRARY_NAME_JUL = "JUL";

    /**
     * Enum value used for {@link #selectLoggerLibrary(int)}; indicates that Avalon LogKit should be used.
     * 
     * @deprecated Avalon LogKit support will be removed sometimes in the future.
     */
    @Deprecated
    public static final int LIBRARY_AVALON = 2;

    /**
     * {@value #SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY} property value; indicates that Avalon LogKit should be used. @since
     * 2.3.22
     * 
     * @deprecated Avalon LogKit support will be removed sometimes in the future.
     */
    // This value is also used as part of the factory class name.
    @Deprecated
    public static final String LIBRARY_NAME_AVALON = "Avalon";

    /**
     * Enum value used for {@link #selectLoggerLibrary(int)}; indicates that Log4J should be used.
     */
    public static final int LIBRARY_LOG4J = 3;

    /**
     * {@value #SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY} property value; indicates that Log4J should be used. @since 2.3.22
     */
    // This value is also used as part of the factory class name.
    public static final String LIBRARY_NAME_LOG4J = "Log4j";

    /**
     * Enum value used for {@link #selectLoggerLibrary(int)}; indicates that Apache commons-logging should be used.
     */
    public static final int LIBRARY_COMMONS = 4;

    /**
     * {@value #SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY} property value; indicates that Apache commons-logging should be
     * used. @since 2.3.22
     */
    // This value is also used as part of the factory class name.
    public static final String LIBRARY_NAME_COMMONS_LOGGING = "CommonsLogging";

    /**
     * Constant used with {@link #selectLoggerLibrary(int)}; indicates that SLF4J should be used.
     */
    public static final int LIBRARY_SLF4J = 5;

    /**
     * {@value #SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY} property value; indicates that SLF4J should be used. @since 2.3.22
     */
    // This value is also used as part of the factory class name.
    public static final String LIBRARY_NAME_SLF4J = "SLF4J";
    private static final int MAX_LIBRARY_ENUM = LIBRARY_SLF4J;

    private static final String REAL_LOG4J_PRESENCE_CLASS = "org.apache.log4j.FileAppender";
    private static final String LOG4J_OVER_SLF4J_TESTER_CLASS = "freemarker.log._Log4jOverSLF4JTester";

    /**
     * 订单事宜!从最低优先级开始。
     */
    private static final String[] LIBRARIES_BY_PRIORITY = {
            null, LIBRARY_NAME_JUL,
            "org.apache.log.Logger", LIBRARY_NAME_AVALON,
            "org.apache.log4j.Logger", LIBRARY_NAME_LOG4J,
            /* In 2.3.x this two is skipped by LIBRARY_AUTO: */
            "org.apache.commons.logging.Log", LIBRARY_NAME_COMMONS_LOGGING,
            "org.slf4j.Logger", LIBRARY_NAME_SLF4J,
    };

    private static String getAvailabilityCheckClassName(int libraryEnum) {
        if (libraryEnum == LIBRARY_AUTO || libraryEnum == LIBRARY_NONE) {
            // Statically linked
            return null;
        }
        return LIBRARIES_BY_PRIORITY[(libraryEnum - 1) * 2];
    }

    static {
        if (LIBRARIES_BY_PRIORITY.length / 2 != MAX_LIBRARY_ENUM) {
            throw new AssertionError();
        }
    }

    private static String getLibraryName(int libraryEnum) {
        if (libraryEnum == LIBRARY_AUTO) {
            return LIBRARY_NAME_AUTO;
        }
        if (libraryEnum == LIBRARY_NONE) {
            return LIBRARY_NAME_NONE;
        }
        return LIBRARIES_BY_PRIORITY[(libraryEnum - 1) * 2 + 1];
    }

    private static boolean isAutoDetected(int libraryEnum) {
        // 2.4: Remove libraryEnum == LIBRARY_SLF4J || libraryEnum == LIBRARY_COMMONS
        return !(libraryEnum == LIBRARY_AUTO || libraryEnum == LIBRARY_NONE
                || libraryEnum == LIBRARY_SLF4J || libraryEnum == LIBRARY_COMMONS);
    }

    private static int libraryEnum;
    private static LoggerFactory loggerFactory;
    private static boolean initializedFromSystemProperty;

    private static String categoryPrefix = "";

    private static final Map loggersByCategory = new HashMap();

    /**
     * Selects the logger library to use, unless it's already specified by the
     * {@value #SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY} system property. Call this early in application initialization
     * phase, before calling any other FreeMarker API-s, since once parts of the FreeMarker library bind to the logging
     * subsystem, the change in this value will have no effect on them.
     * 
     * @param libraryEnum
     *            One of <tt>LIBRARY_...</tt> constants. By default, {@link #LIBRARY_AUTO} is used.
     * 
     * @throws ClassNotFoundException
     *             if an explicit logging library is asked for (that is, not {@value #LIBRARY_AUTO} or
     *             {@value #LIBRARY_NONE}) and it's not found in the classpath.
     * 
     * @deprecated This method isn't reliable, unless you can somehow ensure that you access the FreeMarker classes
     *             first; use the {@value #SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY} Java system property instead, like
     *             {@code java ... -Dorg.freemarker.loggerLibrary=slf4j}. See
     *             {@link #SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY} for more.
     */
    @Deprecated
    public static void selectLoggerLibrary(int libraryEnum) throws ClassNotFoundException {
        if (libraryEnum < MIN_LIBRARY_ENUM || libraryEnum > MAX_LIBRARY_ENUM) {
            throw new IllegalArgumentException("Library enum value out of range");
        }

        synchronized (Logger.class) {
            final boolean loggerFactoryWasAlreadySet = loggerFactory != null;
            if (!loggerFactoryWasAlreadySet || libraryEnum != Logger.libraryEnum) {
                // Creates the factory only if it can be done based on system property:
                ensureLoggerFactorySet(true);

                // The system property has precedence because this method was deprecated by it:
                if (!initializedFromSystemProperty || loggerFactory == null) {
                    int replacedLibraryEnum = Logger.libraryEnum;
                    setLibrary(libraryEnum);
                    loggersByCategory.clear();
                    if (loggerFactoryWasAlreadySet) {
                        logWarnInLogger("Logger library was already set earlier to \""
                                + getLibraryName(replacedLibraryEnum) + "\"; "
                                + "change to \"" + getLibraryName(libraryEnum) + "\" won't effect loggers created "
                                + "earlier.");
                    }
                } else if (libraryEnum != Logger.libraryEnum) {
                    logWarnInLogger("Ignored " + Logger.class.getName() + ".selectLoggerLibrary(\""
                            + getLibraryName(libraryEnum)
                            + "\") call, because the \"" + SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY
                            + "\" system property is set to \"" + getLibraryName(Logger.libraryEnum) + "\".");
                }
            }
        }
    }

    /**
     * Sets a category prefix. This prefix is prepended to any logger category name. This makes it possible to have
     * different FreeMarker logger categories on a per-application basis (better said, per-classloader basis). By
     * default the category prefix is the empty string. If you set a non-empty category prefix, be sure to include the
     * trailing separator dot (i.e. "MyApp.") If you want to change the default setting, do it early in application
     * initialization phase, before calling any other FreeMarker API since once various parts of the FreeMarker library
     * bind to the logging subsystem, the change in this value will have no effect on them.
     * 
     * @deprecated This wasn't reliable, unless you can somehow ensure that you access the FreeMarker classes first. As
     *             it's not known to be useful for users, consider it removed.
     */
    @Deprecated
    public static void setCategoryPrefix(String prefix) {
        synchronized (Logger.class) {
            if (prefix == null) {
                throw new IllegalArgumentException();
            }
            categoryPrefix = prefix;
        }
    }

    /**
     * Logs a debugging message.
     */
    public abstract void debug(String message);

    /**
     * Logs a debugging message with accompanying throwable.
     */
    public abstract void debug(String message, Throwable t);

    /**
     * Logs an informational message.
     */
    public abstract void info(String message);

    /**
     * Logs an informational message with accompanying throwable.
     */
    public abstract void info(String message, Throwable t);

    /**
     * Logs a warning message.
     */
    public abstract void warn(String message);

    /**
     * Logs a warning message with accompanying throwable.
     */
    public abstract void warn(String message, Throwable t);

    /**
     * Logs an error message.
     */
    public abstract void error(String message);

    /**
     * Logs an error message with accompanying throwable.
     */
    public abstract void error(String message, Throwable t);

    /**
     * Returns true if this logger will log debug messages.
     */
    public abstract boolean isDebugEnabled();

    /**
     * Returns true if this logger will log informational messages.
     */
    public abstract boolean isInfoEnabled();

    /**
     * Returns true if this logger will log warning messages.
     */
    public abstract boolean isWarnEnabled();

    /**
     * Returns true if this logger will log error messages.
     */
    public abstract boolean isErrorEnabled();

    /**
     * Returns true if this logger will log fatal error messages.
     */
    public abstract boolean isFatalEnabled();

    /**
     * Returns a logger for the specified category.
     * 
     * @param category
     *            a dot separated hierarchical category name. If a category prefix is in effect, it's prepended to the
     *            category name.
     */
    public static Logger getLogger(String category) {
        if (categoryPrefix.length() != 0) {
            category = categoryPrefix + category;
        }
        synchronized (loggersByCategory) {
            Logger logger = (Logger) loggersByCategory.get(category);
            if (logger == null) {
                ensureLoggerFactorySet(false);
                logger = loggerFactory.getLogger(category);
                loggersByCategory.put(category, logger);
            }
            return logger;
        }
    }

    private static void ensureLoggerFactorySet(boolean onlyIfCanBeSetFromSysProp) {
        if (loggerFactory != null) return;
        synchronized (Logger.class) {
            if (loggerFactory != null) return;

            String sysPropVal = getSystemProperty(SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY);

            final int libraryEnum;
            if (sysPropVal != null) {
                sysPropVal = sysPropVal.trim();

                boolean foundMatch = false;
                int matchedEnum = MIN_LIBRARY_ENUM;
                do {
                    if (sysPropVal.equalsIgnoreCase(getLibraryName(matchedEnum))) {
                        foundMatch = true;
                    } else {
                        matchedEnum++;
                    }
                } while (matchedEnum <= MAX_LIBRARY_ENUM && !foundMatch);

                if (!foundMatch) {
                    logWarnInLogger("Ignored invalid \"" + SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY
                            + "\" system property value: \"" + sysPropVal + "\"");
                    if (onlyIfCanBeSetFromSysProp) return;
                }

                libraryEnum = foundMatch ? matchedEnum : LIBRARY_AUTO;
            } else {
                if (onlyIfCanBeSetFromSysProp) return;
                libraryEnum = LIBRARY_AUTO;
            }

            try {
                setLibrary(libraryEnum);
                if (sysPropVal != null) {
                    initializedFromSystemProperty = true;
                }
            } catch (Throwable e) {
                final boolean disableLogging = !(onlyIfCanBeSetFromSysProp && sysPropVal != null);
                logErrorInLogger(
                        "Couldn't set up logger for \"" + getLibraryName(libraryEnum) + "\""
                                + (disableLogging ? "; logging disabled" : "."), e);
                if (disableLogging) {
                    try {
                        setLibrary(LIBRARY_NONE);
                    } catch (ClassNotFoundException e2) {
                        throw new RuntimeException("Bug", e2);
                    }
                }
            }
        }
    }

    /**
     * @throws ClassNotFoundException
     *             If the requested logger library is not available. Never thrown for {@link #LIBRARY_AUTO}.
     */
    private static LoggerFactory createLoggerFactory(int libraryEnum) throws ClassNotFoundException {
        if (libraryEnum == LIBRARY_AUTO) {
            for (int libraryEnumToTry = MAX_LIBRARY_ENUM; libraryEnumToTry >= MIN_LIBRARY_ENUM; libraryEnumToTry--) {
                if (!isAutoDetected(libraryEnumToTry)) continue;
                if (libraryEnumToTry == LIBRARY_LOG4J && hasLog4LibraryThatDelegatesToWorkingSLF4J()) {
                    libraryEnumToTry = LIBRARY_SLF4J;
                }

                try {
                    return createLoggerFactoryForNonAuto(libraryEnumToTry);
                } catch (ClassNotFoundException e) {
                    // Expected, intentionally suppressed.
                } catch (Throwable e) {
                    logErrorInLogger(
                            "Unexpected error when initializing logging for \""
                                    + getLibraryName(libraryEnumToTry) + "\".",
                            e);
                }
            }
            logWarnInLogger("Auto detecton couldn't set up any logger libraries; FreeMarker logging suppressed.");
            return new _NullLoggerFactory();
        } else {
            return createLoggerFactoryForNonAuto(libraryEnum);
        }
    }

    /**
     * @throws ClassNotFoundException
     *             If the required logger library is not available.
     */
    private static LoggerFactory createLoggerFactoryForNonAuto(int libraryEnum) throws ClassNotFoundException {
        final String availabilityCheckClassName = getAvailabilityCheckClassName(libraryEnum);
        if (availabilityCheckClassName != null) { // Dynamically created factory
            Class.forName(availabilityCheckClassName);
            String libraryName = getLibraryName(libraryEnum);
            try {
                return (LoggerFactory) Class.forName(
                        "freemarker.log._" + libraryName + "LoggerFactory").newInstance();
            } catch (Exception e) {
                throw new RuntimeException(
                        "Unexpected error when creating logger factory for \"" + libraryName + "\".", e);
            }
        } else { // Non-dynamically created factory
            if (libraryEnum == LIBRARY_JAVA) {
                return new _JULLoggerFactory();
            } else if (libraryEnum == LIBRARY_NONE) {
                return new _NullLoggerFactory();
            } else {
                throw new RuntimeException("Bug");
            }
        }
    }

    private static boolean hasLog4LibraryThatDelegatesToWorkingSLF4J() {
        try {
            Class.forName(getAvailabilityCheckClassName(LIBRARY_LOG4J));
            Class.forName(getAvailabilityCheckClassName(LIBRARY_SLF4J));
        } catch (Throwable e) {
            return false;
        }
        try {
            Class.forName(REAL_LOG4J_PRESENCE_CLASS);
            return false;
        } catch (ClassNotFoundException e) {
            try {
                Object r = Class.forName(LOG4J_OVER_SLF4J_TESTER_CLASS)
                        .getMethod("test", new Class[] {}).invoke(null);
                return ((Boolean) r).booleanValue();
            } catch (Throwable e2) {
                return false;
            }
        }
    }

    private synchronized static void setLibrary(int libraryEnum) throws ClassNotFoundException {
        loggerFactory = createLoggerFactory(libraryEnum);
        Logger.libraryEnum = libraryEnum;
    }

    private static void logWarnInLogger(String message) {
        logInLogger(false, message, null);
    }

    private static void logErrorInLogger(String message, Throwable exception) {
        logInLogger(true, message, exception);
    }

    private static void logInLogger(boolean error, String message, Throwable exception) {
        boolean canUseRealLogger;
        synchronized (Logger.class) {
            canUseRealLogger = loggerFactory != null && !(loggerFactory instanceof _NullLoggerFactory);
        }

        if (canUseRealLogger) {
            try {
                final Logger logger = Logger.getLogger("freemarker.logger");
                if (error) {
                    logger.error(message);
                } else {
                    logger.warn(message);
                }
            } catch (Throwable e) {
                canUseRealLogger = false;
            }
        }

        if (!canUseRealLogger) {
            System.err.println((error ? "ERROR" : "WARN") + " "
                    + LoggerFactory.class.getName() + ": " + message);
            if (exception != null) {
                System.err.println("\tException: " + tryToString(exception));
                while (exception.getCause() != null) {
                    exception = exception.getCause();
                    System.err.println("\tCaused by: " + tryToString(exception));
                }
            }
        }
    }

    /**
     * Don't use {@link freemarker.template.utility.SecurityUtilities#getSystemProperty(String, String)} here, as it
     * (might) depends on the logger, hence interfering with the initialization.
     */
    private static String getSystemProperty(final String key) {
        try {
            return (String) AccessController.doPrivileged(
                    new PrivilegedAction() {
                        @Override
                        public Object run() {
                            return System.getProperty(key, null);
                        }
                    });
        } catch (AccessControlException e) {
            logWarnInLogger("Insufficient permissions to read system property \"" + key + "\".");
            return null;
        } catch (Throwable e) {
            logErrorInLogger("Failed to read system property \"" + key + "\".", e);
            return null;
        }
    }

    /**
     * Don't use {@link freemarker.template.utility.StringUtil#tryToString(Object)} here, as it might depends on the
     * logger, hence interfering with the initialization.
     */
    private static String tryToString(Object object) {
        if (object == null) return null;
        try {
            return object.toString();
        } catch (Throwable e) {
            return object.getClass().getName();
        }
    }

}

源码分析

getLibraryName()方法主要是获取libraryEum做对应的LibraryName的名字并且以字符串的形式返回;

isAutoDetected()方法主要是检查libraryEum所对应,只要与四个其中的一个可以对应上,就会返回一个FALSE,证明可以匹配。


    private static String getLibraryName(int libraryEnum) {
        if (libraryEnum == LIBRARY_AUTO) {
            return LIBRARY_NAME_AUTO;
        }
        if (libraryEnum == LIBRARY_NONE) {
            return LIBRARY_NAME_NONE;
        }
        return LIBRARIES_BY_PRIORITY[(libraryEnum - 1) * 2 + 1];
    }

    private static boolean isAutoDetected(int libraryEnum) {
        // 2.4: Remove libraryEnum == LIBRARY_SLF4J || libraryEnum == LIBRARY_COMMONS
        return !(libraryEnum == LIBRARY_AUTO || libraryEnum == LIBRARY_NONE
                || libraryEnum == LIBRARY_SLF4J || libraryEnum == LIBRARY_COMMONS);
    }

selectLoogerLibrary()用于选择LoggerLibrary通过LibraryEnum的编号进行查找,如果查找不到则会返回一个Exception

 public static void selectLoggerLibrary(int libraryEnum) throws ClassNotFoundException {
        if (libraryEnum < MIN_LIBRARY_ENUM || libraryEnum > MAX_LIBRARY_ENUM) {
            throw new IllegalArgumentException("Library enum value out of range");
        }

        synchronized (Logger.class) {
            final boolean loggerFactoryWasAlreadySet = loggerFactory != null;
            if (!loggerFactoryWasAlreadySet || libraryEnum != Logger.libraryEnum) {
                // Creates the factory only if it can be done based on system property:
                ensureLoggerFactorySet(true);

                // The system property has precedence because this method was deprecated by it:
                if (!initializedFromSystemProperty || loggerFactory == null) {
                    int replacedLibraryEnum = Logger.libraryEnum;
                    setLibrary(libraryEnum);
                    loggersByCategory.clear();
                    if (loggerFactoryWasAlreadySet) {
                        logWarnInLogger("Logger library was already set earlier to \""
                                + getLibraryName(replacedLibraryEnum) + "\"; "
                                + "change to \"" + getLibraryName(libraryEnum) + "\" won't effect loggers created "
                                + "earlier.");
                    }
                } else if (libraryEnum != Logger.libraryEnum) {
                    logWarnInLogger("Ignored " + Logger.class.getName() + ".selectLoggerLibrary(\""
                            + getLibraryName(libraryEnum)
                            + "\") call, because the \"" + SYSTEM_PROPERTY_NAME_LOGGER_LIBRARY
                            + "\" system property is set to \"" + getLibraryName(Logger.libraryEnum) + "\".");
                }
            }
        }
    }

createLoggerFactory()主要是确定选择哪一个LoggerFactory,然后去调用之实现的各种LoggerFactory 去生产Logger实例。

private static LoggerFactory createLoggerFactory(int libraryEnum) throws ClassNotFoundException {
        if (libraryEnum == LIBRARY_AUTO) {
            for (int libraryEnumToTry = MAX_LIBRARY_ENUM; libraryEnumToTry >= MIN_LIBRARY_ENUM; libraryEnumToTry--) {
                if (!isAutoDetected(libraryEnumToTry)) continue;
                if (libraryEnumToTry == LIBRARY_LOG4J && hasLog4LibraryThatDelegatesToWorkingSLF4J()) {
                    libraryEnumToTry = LIBRARY_SLF4J;
                }

                try {
                    return createLoggerFactoryForNonAuto(libraryEnumToTry);
                } catch (ClassNotFoundException e) {
                    // Expected, intentionally suppressed.
                } catch (Throwable e) {
                    logErrorInLogger(
                            "Unexpected error when initializing logging for \""
                                    + getLibraryName(libraryEnumToTry) + "\".",
                            e);
                }
            }
            logWarnInLogger("Auto detecton couldn't set up any logger libraries; FreeMarker logging suppressed.");
            return new _NullLoggerFactory();
        } else {
            return createLoggerFactoryForNonAuto(libraryEnum);
        }
    }

lodInLogger()方法用于捕捉并处理出现的错误,并且打印出来程序执行错误的原因。

private static void logInLogger(boolean error, String message, Throwable exception) {
        boolean canUseRealLogger;
        synchronized (Logger.class) {
            canUseRealLogger = loggerFactory != null && !(loggerFactory instanceof _NullLoggerFactory);
        }

        if (canUseRealLogger) {
            try {
                final Logger logger = Logger.getLogger("freemarker.logger");
                if (error) {
                    logger.error(message);
                } else {
                    logger.warn(message);
                }
            } catch (Throwable e) {
                canUseRealLogger = false;
            }
        }

        if (!canUseRealLogger) {
            System.err.println((error ? "ERROR" : "WARN") + " "
                    + LoggerFactory.class.getName() + ": " + message);
            if (exception != null) {
                System.err.println("\tException: " + tryToString(exception));
                while (exception.getCause() != null) {
                    exception = exception.getCause();
                    System.err.println("\tCaused by: " + tryToString(exception));
                }
            }
        }
    }

打印信息

 private static String tryToString(Object object) {
        if (object == null) return null;
        try {
            return object.toString();
        } catch (Throwable e) {
            return object.getClass().getName();
        }
    }


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值