美文网首页
spring-jcl 日志学习

spring-jcl 日志学习

作者: dbasy | 来源:发表于2021-05-21 10:32 被阅读0次

在Java项目开发中,日志是必不可少的功能,日志对于快速定位问题,检查日常项目运行状态等有非常重要的作用,但是目前Java日志存在多种框架,如:Slf4j、JUL、JCL、Log4j、Log4j2、Logback等。

框架简介

  • Slf4j 日志门面组件
  • JCL Commons Logging,简称jcl,Apache基金会项目,日志门面组件
  • Log4j Apache基金会项目,日志实现框架
  • Log4j 2 是Log4j的升级产品,但是与Log4j不兼容
  • Logback 日志实现框架
  • JUL java官方的日志实现框架

Java中可用的日志框架有很多,这样就导致一个选择困难问题,到底应该用哪一个框架,如果项目修改日志组件或者升级又该如何做。其实一般都会选择使用外观模式:日志门面组件+桥接器+日志实现框架,这样即使项目更换日志种类,只需更换桥接器和日志实现框架,也就是只更换Jar包就可以了,代码无需做任何改动。

spring-jcl使用得是工厂创建日志实例,通过适配器来自动选择日志实现方式,换句话说就是在类路径放入不同的日志jar包,spring会选择不同得日志实现。

LogFactory类,去除了不推荐的方法
public abstract class LogFactory {

     /**
     * Convenience method to return a named logger.
     * @param clazz containing Class from which a log name will be derived
     */
    public static Log getLog(Class<?> clazz) {
        return getLog(clazz.getName());
    }

    /**
     * Convenience method to return a named logger.
     * @param name logical name of the <code>Log</code> instance to be returned
     */
    public static Log getLog(String name) {
        return LogAdapter.createLog(name);
    }
}
LogAdapter
final class LogAdapter {

    private static final String LOG4J_SPI = "org.apache.logging.log4j.spi.ExtendedLogger";

    private static final String LOG4J_SLF4J_PROVIDER = "org.apache.logging.slf4j.SLF4JProvider";

    private static final String SLF4J_SPI = "org.slf4j.spi.LocationAwareLogger";

    private static final String SLF4J_API = "org.slf4j.Logger";


    private static final LogApi logApi;

    static {
        if (isPresent(LOG4J_SPI)) {
            if (isPresent(LOG4J_SLF4J_PROVIDER) && isPresent(SLF4J_SPI)) {
                // log4j-to-slf4j bridge -> we'll rather go with the SLF4J SPI;
                // however, we still prefer Log4j over the plain SLF4J API since
                // the latter does not have location awareness support.
                logApi = LogApi.SLF4J_LAL;
            }
            else {
                // Use Log4j 2.x directly, including location awareness support
                logApi = LogApi.LOG4J;
            }
        }
        else if (isPresent(SLF4J_SPI)) {
            // Full SLF4J SPI including location awareness support
            logApi = LogApi.SLF4J_LAL;
        }
        else if (isPresent(SLF4J_API)) {
            // Minimal SLF4J API without location awareness support
            logApi = LogApi.SLF4J;
        }
        else {
            // java.util.logging as default
            logApi = LogApi.JUL;
        }
    }


    private LogAdapter() {
    }


    /**
     * Create an actual {@link Log} instance for the selected API.
     * @param name the logger name
     */
    public static Log createLog(String name) {
        switch (logApi) {
            case LOG4J:
                return Log4jAdapter.createLog(name);
            case SLF4J_LAL:
                return Slf4jAdapter.createLocationAwareLog(name);
            case SLF4J:
                return Slf4jAdapter.createLog(name);
            default:
                // Defensively use lazy-initializing adapter class here as well since the
                // java.logging module is not present by default on JDK 9. We are requiring
                // its presence if neither Log4j nor SLF4J is available; however, in the
                // case of Log4j or SLF4J, we are trying to prevent early initialization
                // of the JavaUtilLog adapter - e.g. by a JVM in debug mode - when eagerly
                // trying to parse the bytecode for all the cases of this switch clause.
                return JavaUtilAdapter.createLog(name);
        }
    }

    private static boolean isPresent(String className) {
        try {
            Class.forName(className, false, LogAdapter.class.getClassLoader());
            return true;
        }
        catch (ClassNotFoundException ex) {
            return false;
        }
    }


    private enum LogApi {LOG4J, SLF4J_LAL, SLF4J, JUL}


    private static class Log4jAdapter {

        public static Log createLog(String name) {
            return new Log4jLog(name);
        }
    }


    private static class Slf4jAdapter {

        public static Log createLocationAwareLog(String name) {
            Logger logger = LoggerFactory.getLogger(name);
            return (logger instanceof LocationAwareLogger ?
                    new Slf4jLocationAwareLog((LocationAwareLogger) logger) : new Slf4jLog<>(logger));
        }

        public static Log createLog(String name) {
            return new Slf4jLog<>(LoggerFactory.getLogger(name));
        }
    }


    private static class JavaUtilAdapter {

        public static Log createLog(String name) {
            return new JavaUtilLog(name);
        }
    }


    @SuppressWarnings("serial")
    private static class Log4jLog implements Log, Serializable {

        private static final String FQCN = Log4jLog.class.getName();

        private static final LoggerContext loggerContext =
                LogManager.getContext(Log4jLog.class.getClassLoader(), false);

        private final ExtendedLogger logger;

        public Log4jLog(String name) {
            LoggerContext context = loggerContext;
            if (context == null) {
                // Circular call in early-init scenario -> static field not initialized yet
                context = LogManager.getContext(Log4jLog.class.getClassLoader(), false);
            }
            this.logger = context.getLogger(name);
        }

        @Override
        public boolean isFatalEnabled() {
            return this.logger.isEnabled(Level.FATAL);
        }

        @Override
        public boolean isErrorEnabled() {
            return this.logger.isEnabled(Level.ERROR);
        }

        @Override
        public boolean isWarnEnabled() {
            return this.logger.isEnabled(Level.WARN);
        }

        @Override
        public boolean isInfoEnabled() {
            return this.logger.isEnabled(Level.INFO);
        }

        @Override
        public boolean isDebugEnabled() {
            return this.logger.isEnabled(Level.DEBUG);
        }

        @Override
        public boolean isTraceEnabled() {
            return this.logger.isEnabled(Level.TRACE);
        }

        @Override
        public void fatal(Object message) {
            log(Level.FATAL, message, null);
        }

        @Override
        public void fatal(Object message, Throwable exception) {
            log(Level.FATAL, message, exception);
        }

        @Override
        public void error(Object message) {
            log(Level.ERROR, message, null);
        }

        @Override
        public void error(Object message, Throwable exception) {
            log(Level.ERROR, message, exception);
        }

        @Override
        public void warn(Object message) {
            log(Level.WARN, message, null);
        }

        @Override
        public void warn(Object message, Throwable exception) {
            log(Level.WARN, message, exception);
        }

        @Override
        public void info(Object message) {
            log(Level.INFO, message, null);
        }

        @Override
        public void info(Object message, Throwable exception) {
            log(Level.INFO, message, exception);
        }

        @Override
        public void debug(Object message) {
            log(Level.DEBUG, message, null);
        }

        @Override
        public void debug(Object message, Throwable exception) {
            log(Level.DEBUG, message, exception);
        }

        @Override
        public void trace(Object message) {
            log(Level.TRACE, message, null);
        }

        @Override
        public void trace(Object message, Throwable exception) {
            log(Level.TRACE, message, exception);
        }

        private void log(Level level, Object message, Throwable exception) {
            if (message instanceof String) {
                // Explicitly pass a String argument, avoiding Log4j's argument expansion
                // for message objects in case of "{}" sequences (SPR-16226)
                if (exception != null) {
                    this.logger.logIfEnabled(FQCN, level, null, (String) message, exception);
                }
                else {
                    this.logger.logIfEnabled(FQCN, level, null, (String) message);
                }
            }
            else {
                this.logger.logIfEnabled(FQCN, level, null, message, exception);
            }
        }
    }


    @SuppressWarnings("serial")
    private static class Slf4jLog<T extends Logger> implements Log, Serializable {

        protected final String name;

        protected transient T logger;

        public Slf4jLog(T logger) {
            this.name = logger.getName();
            this.logger = logger;
        }

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

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

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

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

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

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

        @Override
        public void fatal(Object message) {
            error(message);
        }

        @Override
        public void fatal(Object message, Throwable exception) {
            error(message, exception);
        }

        @Override
        public void error(Object message) {
            if (message instanceof String || this.logger.isErrorEnabled()) {
                this.logger.error(String.valueOf(message));
            }
        }

        @Override
        public void error(Object message, Throwable exception) {
            if (message instanceof String || this.logger.isErrorEnabled()) {
                this.logger.error(String.valueOf(message), exception);
            }
        }

        @Override
        public void warn(Object message) {
            if (message instanceof String || this.logger.isWarnEnabled()) {
                this.logger.warn(String.valueOf(message));
            }
        }

        @Override
        public void warn(Object message, Throwable exception) {
            if (message instanceof String || this.logger.isWarnEnabled()) {
                this.logger.warn(String.valueOf(message), exception);
            }
        }

        @Override
        public void info(Object message) {
            if (message instanceof String || this.logger.isInfoEnabled()) {
                this.logger.info(String.valueOf(message));
            }
        }

        @Override
        public void info(Object message, Throwable exception) {
            if (message instanceof String || this.logger.isInfoEnabled()) {
                this.logger.info(String.valueOf(message), exception);
            }
        }

        @Override
        public void debug(Object message) {
            if (message instanceof String || this.logger.isDebugEnabled()) {
                this.logger.debug(String.valueOf(message));
            }
        }

        @Override
        public void debug(Object message, Throwable exception) {
            if (message instanceof String || this.logger.isDebugEnabled()) {
                this.logger.debug(String.valueOf(message), exception);
            }
        }

        @Override
        public void trace(Object message) {
            if (message instanceof String || this.logger.isTraceEnabled()) {
                this.logger.trace(String.valueOf(message));
            }
        }

        @Override
        public void trace(Object message, Throwable exception) {
            if (message instanceof String || this.logger.isTraceEnabled()) {
                this.logger.trace(String.valueOf(message), exception);
            }
        }

        protected Object readResolve() {
            return Slf4jAdapter.createLog(this.name);
        }
    }


    @SuppressWarnings("serial")
    private static class Slf4jLocationAwareLog extends Slf4jLog<LocationAwareLogger> implements Serializable {

        private static final String FQCN = Slf4jLocationAwareLog.class.getName();

        public Slf4jLocationAwareLog(LocationAwareLogger logger) {
            super(logger);
        }

        @Override
        public void fatal(Object message) {
            error(message);
        }

        @Override
        public void fatal(Object message, Throwable exception) {
            error(message, exception);
        }

        @Override
        public void error(Object message) {
            if (message instanceof String || this.logger.isErrorEnabled()) {
                this.logger.log(null, FQCN, LocationAwareLogger.ERROR_INT, String.valueOf(message), null, null);
            }
        }

        @Override
        public void error(Object message, Throwable exception) {
            if (message instanceof String || this.logger.isErrorEnabled()) {
                this.logger.log(null, FQCN, LocationAwareLogger.ERROR_INT, String.valueOf(message), null, exception);
            }
        }

        @Override
        public void warn(Object message) {
            if (message instanceof String || this.logger.isWarnEnabled()) {
                this.logger.log(null, FQCN, LocationAwareLogger.WARN_INT, String.valueOf(message), null, null);
            }
        }

        @Override
        public void warn(Object message, Throwable exception) {
            if (message instanceof String || this.logger.isWarnEnabled()) {
                this.logger.log(null, FQCN, LocationAwareLogger.WARN_INT, String.valueOf(message), null, exception);
            }
        }

        @Override
        public void info(Object message) {
            if (message instanceof String || this.logger.isInfoEnabled()) {
                this.logger.log(null, FQCN, LocationAwareLogger.INFO_INT, String.valueOf(message), null, null);
            }
        }

        @Override
        public void info(Object message, Throwable exception) {
            if (message instanceof String || this.logger.isInfoEnabled()) {
                this.logger.log(null, FQCN, LocationAwareLogger.INFO_INT, String.valueOf(message), null, exception);
            }
        }

        @Override
        public void debug(Object message) {
            if (message instanceof String || this.logger.isDebugEnabled()) {
                this.logger.log(null, FQCN, LocationAwareLogger.DEBUG_INT, String.valueOf(message), null, null);
            }
        }

        @Override
        public void debug(Object message, Throwable exception) {
            if (message instanceof String || this.logger.isDebugEnabled()) {
                this.logger.log(null, FQCN, LocationAwareLogger.DEBUG_INT, String.valueOf(message), null, exception);
            }
        }

        @Override
        public void trace(Object message) {
            if (message instanceof String || this.logger.isTraceEnabled()) {
                this.logger.log(null, FQCN, LocationAwareLogger.TRACE_INT, String.valueOf(message), null, null);
            }
        }

        @Override
        public void trace(Object message, Throwable exception) {
            if (message instanceof String || this.logger.isTraceEnabled()) {
                this.logger.log(null, FQCN, LocationAwareLogger.TRACE_INT, String.valueOf(message), null, exception);
            }
        }

        @Override
        protected Object readResolve() {
            return Slf4jAdapter.createLocationAwareLog(this.name);
        }
    }


    @SuppressWarnings("serial")
    private static class JavaUtilLog implements Log, Serializable {

        private String name;

        private transient java.util.logging.Logger logger;

        public JavaUtilLog(String name) {
            this.name = name;
            this.logger = java.util.logging.Logger.getLogger(name);
        }

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

        @Override
        public boolean isErrorEnabled() {
            return this.logger.isLoggable(java.util.logging.Level.SEVERE);
        }

        @Override
        public boolean isWarnEnabled() {
            return this.logger.isLoggable(java.util.logging.Level.WARNING);
        }

        @Override
        public boolean isInfoEnabled() {
            return this.logger.isLoggable(java.util.logging.Level.INFO);
        }

        @Override
        public boolean isDebugEnabled() {
            return this.logger.isLoggable(java.util.logging.Level.FINE);
        }

        @Override
        public boolean isTraceEnabled() {
            return this.logger.isLoggable(java.util.logging.Level.FINEST);
        }

        @Override
        public void fatal(Object message) {
            error(message);
        }

        @Override
        public void fatal(Object message, Throwable exception) {
            error(message, exception);
        }

        @Override
        public void error(Object message) {
            log(java.util.logging.Level.SEVERE, message, null);
        }

        @Override
        public void error(Object message, Throwable exception) {
            log(java.util.logging.Level.SEVERE, message, exception);
        }

        @Override
        public void warn(Object message) {
            log(java.util.logging.Level.WARNING, message, null);
        }

        @Override
        public void warn(Object message, Throwable exception) {
            log(java.util.logging.Level.WARNING, message, exception);
        }

        @Override
        public void info(Object message) {
            log(java.util.logging.Level.INFO, message, null);
        }

        @Override
        public void info(Object message, Throwable exception) {
            log(java.util.logging.Level.INFO, message, exception);
        }

        @Override
        public void debug(Object message) {
            log(java.util.logging.Level.FINE, message, null);
        }

        @Override
        public void debug(Object message, Throwable exception) {
            log(java.util.logging.Level.FINE, message, exception);
        }

        @Override
        public void trace(Object message) {
            log(java.util.logging.Level.FINEST, message, null);
        }

        @Override
        public void trace(Object message, Throwable exception) {
            log(java.util.logging.Level.FINEST, message, exception);
        }

        private void log(java.util.logging.Level level, Object message, Throwable exception) {
            if (this.logger.isLoggable(level)) {
                LogRecord rec;
                if (message instanceof LogRecord) {
                    rec = (LogRecord) message;
                }
                else {
                    rec = new LocationResolvingLogRecord(level, String.valueOf(message));
                    rec.setLoggerName(this.name);
                    rec.setResourceBundleName(this.logger.getResourceBundleName());
                    rec.setResourceBundle(this.logger.getResourceBundle());
                    rec.setThrown(exception);
                }
                logger.log(rec);
            }
        }

        protected Object readResolve() {
            return new JavaUtilLog(this.name);
        }
    }


    @SuppressWarnings("serial")
    private static class LocationResolvingLogRecord extends LogRecord {

        private static final String FQCN = JavaUtilLog.class.getName();

        private volatile boolean resolved;

        public LocationResolvingLogRecord(java.util.logging.Level level, String msg) {
            super(level, msg);
        }

        @Override
        public String getSourceClassName() {
            if (!this.resolved) {
                resolve();
            }
            return super.getSourceClassName();
        }

        @Override
        public void setSourceClassName(String sourceClassName) {
            super.setSourceClassName(sourceClassName);
            this.resolved = true;
        }

        @Override
        public String getSourceMethodName() {
            if (!this.resolved) {
                resolve();
            }
            return super.getSourceMethodName();
        }

        @Override
        public void setSourceMethodName(String sourceMethodName) {
            super.setSourceMethodName(sourceMethodName);
            this.resolved = true;
        }

        private void resolve() {
            StackTraceElement[] stack = new Throwable().getStackTrace();
            String sourceClassName = null;
            String sourceMethodName = null;
            boolean found = false;
            for (StackTraceElement element : stack) {
                String className = element.getClassName();
                if (FQCN.equals(className)) {
                    found = true;
                }
                else if (found) {
                    sourceClassName = className;
                    sourceMethodName = element.getMethodName();
                    break;
                }
            }
            setSourceClassName(sourceClassName);
            setSourceMethodName(sourceMethodName);
        }

        @SuppressWarnings("deprecation")  // setMillis is deprecated in JDK 9
        protected Object writeReplace() {
            LogRecord serialized = new LogRecord(getLevel(), getMessage());
            serialized.setLoggerName(getLoggerName());
            serialized.setResourceBundle(getResourceBundle());
            serialized.setResourceBundleName(getResourceBundleName());
            serialized.setSourceClassName(getSourceClassName());
            serialized.setSourceMethodName(getSourceMethodName());
            serialized.setSequenceNumber(getSequenceNumber());
            serialized.setParameters(getParameters());
            serialized.setThreadID(getThreadID());
            serialized.setMillis(getMillis());
            serialized.setThrown(getThrown());
            return serialized;
        }
    }

}

#######总结
先找log4j再找slf4j,现在主流日志是slf4j+logback组合,slf4j充当门面,logback具体日志实现框架。
回顾我们平时使用日志的方式,就可以兼容各种日志框架,而不需要更改代码

public class LogDemo {
    Log log = LogFactory.getLog(LogDemo.class);

    public void logLevel() {
        log.debug("debug");
        log.warn("warn");
        log.trace("trace");
        log.info("debug");
        log.error("debug");
        log.fatal("debug");
        log.debug("debug");

    }
}

相关文章

  • spring-jcl 日志学习

    在Java项目开发中,日志是必不可少的功能,日志对于快速定位问题,检查日常项目运行状态等有非常重要的作用,但是目前...

  • Springboot + logback源码

    springboot的日志启动步骤主要分以下几步: Spring-jcl包,主要接口是org.apache.com...

  • 关于Spring-JCL日志的坑

    项目中用到Google的客户端库,必须要依赖log4j和slf4j-log4j12,在官网下载的sample能正常...

  • 真善美

    【崔律·销售答疑课·学习日志】 这是2019年4月4日“崔律精时力”课之“销售·答疑课” 的学习日志。 <学习日志...

  • 2019-04-04【崔律•销售答疑课•学习日志】

    【崔律·销售答疑课·学习日志】 这是2019年4月4日“崔律精时力”课之“销售·答疑课”的学习日志。 <学习日志>...

  • 崔律·销售答疑课·学习日志

    【崔律·销售答疑课·学习日志】 这是2019年4月4日“崔律精时力”课之“销售·答疑课” 的学习日志。 <学习日志...

  • java学习笔记-日志篇

    java学习笔记-日志篇-目录 java日志篇(1)-日志概述 java日志篇(2)-JUL(java.util....

  • 2017.1.10

    今日学习: 日志系统 和 react基础学习 和 vela 日志系统:cetus 是一个将源位置的日志通过filt...

  • 改变常规思维—销售·答疑课

    【崔律·销售答疑课·学习日志】这是2019年4月4日“崔律精时力”课之“销售·答疑课” 的学习日志。<学习日志>1...

  • 2021-08-11学习力(2021年)·(加餐)01真学习则要

    【精时力学习日志】 本训练营:学习力(2021年) 今日主题:(加餐)01真学习则要写日志 学习日期:2021年8...

网友评论

      本文标题:spring-jcl 日志学习

      本文链接:https://www.haomeiwen.com/subject/rxymjltx.html