zoukankan      html  css  js  c++  java
  • 实现jul 日志重定向到 slf4j

    需求背景

        jul 指的是java.util.logging,是 java 内置的日志模块,目前流行的Java日志组件还包括 jcl(common-logging)、slf4j/log4j/logback 等等 
    不同日志框架的定位和特性都存在差异,如 jcl、slf4j 提供的是日志门面(api)定义,log4j、logback则侧重于实现。

    通常一个团队会采用统一的日志组件,slf4j 目前的受欢迎程度较高,其在易用性、可移植性方面都优于jul; 
    然而项目中采用的一些开源组件可能直接采用了jul 进行日志输出,为保证日志的统一配置管理,需将其迁移到slf4j 日志框架上;

    关键要求

    1. 不改动现有开源组件代码;

    2. 按需进行迁移,不影响其他模块的 logging 记录;

    3. 模块支持可插拔,可动态集成和撤销;

    方案分析

    java.util.logging 架构定义如下: 
    overview-of-control-flow-oracle 


    Logger 以名称(如package) 为标识,Logger之间为树级结构,与log4j类似; 
    Handler 接口实现了真正的日志处理,如实现过滤、输出到文件、网络IO..

    复制代码
    public abstract class Handler{
    
        /**
         * Publish a <tt>LogRecord</tt>.
         * <p>
         * The logging request was made initially to a <tt>Logger</tt> object,
         * which initialized the <tt>LogRecord</tt> and forwarded it here.
         * <p>
         * The <tt>Handler</tt>  is responsible for formatting the message, when and
         * if necessary.  The formatting should include localization.
         *
         * @param  record  description of the log event. A null record is
         *                 silently ignored and is not published
         */
        public abstract void publish(LogRecord record); 
    
    }
    复制代码

    为实现slf4j 的桥接,考虑以下方法: 
    1 定义日志级别映射,将jul level 映射为 slf4j level; 
    比如

    FINEST/FINER/FINE/=TRACE
    CONFIG=DEBUG
    INFO=INFO
    WARNING=WARN
    SEVERE=ERROR

    2 自定义jul 的日志handler, 将jul LogRecord 使用slf4j 进行输出; 
    3 为避免所有的日志都生成LogRecord对象产生内存浪费,需提前为jul Logger 设置过滤级别;

    代码样例

    1. LoggerLevel 映射定义

    复制代码
    public enum LoggerLevel {
    
        TRACE(Level.ALL),
        DEBUG(Level.CONFIG),
        INFO(Level.INFO),
        WARN(Level.WARNING),
        ERROR(Level.SEVERE);
    
        private Level julLevel;
    
        private LoggerLevel(Level julLevel) {
            this.julLevel = julLevel;
        }
    
        public Level getJulLevel() {
            return this.julLevel;
        }
    }
    复制代码

    2. JulLoggerWrapper 实现 Handler

    复制代码
    public class JulLoggerWrapper extends Handler {
    
        // SEVERE > WARNING > INFO > CONFIG > FINE > FINER > FINEST
        // ERROR > WARN > INFO > DEBUG
        private static final int TRACE_LEVEL_THRESHOLD = Level.FINEST.intValue() - 1;
        private static final int DEBUG_LEVEL_THRESHOLD = Level.CONFIG.intValue();
        private static final int INFO_LEVEL_THRESHOLD = Level.INFO.intValue();
        private static final int WARN_LEVEL_THRESHOLD = Level.WARNING.intValue();
    
        private List<Handler> julHandlers;
        private boolean julUseParentHandlers = false;
        private Level julLevel;
        private Level targetLevel;
        private String name;
    
        public JulLoggerWrapper(String name) {
            this.name = name;
        }
    
        /**
         * install the wrapper
         */
        public void install() {
            java.util.logging.Logger julLogger = this.getJulLogger();
    
            // remove old handlers
            julHandlers = new ArrayList<Handler>();
            for (Handler handler : julLogger.getHandlers()) {
                julHandlers.add(handler);
                julLogger.removeHandler(handler);
            }
    
            // disable parent handler
            this.julUseParentHandlers = julLogger.getUseParentHandlers();
            julLogger.setUseParentHandlers(false);
    
            // record previous level
            this.julLevel = julLogger.getLevel();
            if (this.targetLevel != null) {
                julLogger.setLevel(this.targetLevel);
            }
    
            // install wrapper
            julLogger.addHandler(this);
        }
    
        /**
         * uninstall the wrapper
         */
        public void uninstall() {
            java.util.logging.Logger julLogger = this.getJulLogger();
    
            // uninstall wrapper
            for (Handler handler : julLogger.getHandlers()) {
                if (handler == this) {
                    julLogger.removeHandler(handler);
                }
            }
    
            // recover work..
            julLogger.setUseParentHandlers(this.julUseParentHandlers);
    
            if (this.julLevel != null) {
                julLogger.setLevel(julLevel);
            }
    
            if (this.julHandlers != null) {
                for (Handler handler : this.julHandlers) {
                    julLogger.addHandler(handler);
                }
                this.julHandlers = null;
            }
        }
    
        private java.util.logging.Logger getJulLogger() {
            return java.util.logging.Logger.getLogger(name);
        }
    
        private Logger getWrappedLogger() {
            return LoggerFactory.getLogger(name);
        }
    
        /**
         * 更新级别
         * 
         * @param targetLevel
         */
        public void updateLevel(LoggerLevel targetLevel) {
            if (targetLevel == null) {
                return;
            }
    
            updateLevel(targetLevel.getJulLevel());
        }
    
        /**
         * 更新级别
         * 
         * @param targetLevel
         */
        public void updateLevel(Level targetLevel) {
            if (targetLevel == null) {
                return;
            }
    
            java.util.logging.Logger julLogger = this.getJulLogger();
            if (this.julLevel == null) {
                this.julLevel = julLogger.getLevel();
            }
    
            this.targetLevel = targetLevel;
            julLogger.setLevel(this.targetLevel);
        }
    
        @Override
        public void publish(LogRecord record) {
            // Silently ignore null records.
            if (record == null) {
                return;
            }
    
            Logger wrappedLogger = getWrappedLogger();
            String message = record.getMessage();
    
            if (message == null) {
                message = "";
            }
    
            if (wrappedLogger instanceof LocationAwareLogger) {
                callWithLocationAwareMode((LocationAwareLogger) wrappedLogger, record);
            } else {
                callWithPlainMode(wrappedLogger, record);
            }
        }
    
        /**
         * get the record's i18n message
         *
         * @param record
         * @return
         */
        private String getMessageI18N(LogRecord record) {
            String message = record.getMessage();
    
            if (message == null) {
                return null;
            }
    
            ResourceBundle bundle = record.getResourceBundle();
            if (bundle != null) {
                try {
                    message = bundle.getString(message);
                } catch (MissingResourceException e) {
                }
            }
            Object[] params = record.getParameters();
            // avoid formatting when 0 parameters.
            if (params != null && params.length > 0) {
                try {
                    message = MessageFormat.format(message, params);
                } catch (RuntimeException e) {
                }
            }
            return message;
        }
    
        private void callWithPlainMode(Logger slf4jLogger, LogRecord record) {
    
            String i18nMessage = getMessageI18N(record);
            int julLevelValue = record.getLevel().intValue();
    
            if (julLevelValue <= TRACE_LEVEL_THRESHOLD) {
                slf4jLogger.trace(i18nMessage, record.getThrown());
            } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) {
                slf4jLogger.debug(i18nMessage, record.getThrown());
            } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) {
                slf4jLogger.info(i18nMessage, record.getThrown());
            } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) {
                slf4jLogger.warn(i18nMessage, record.getThrown());
            } else {
                slf4jLogger.error(i18nMessage, record.getThrown());
            }
        }
    
        private void callWithLocationAwareMode(LocationAwareLogger lal, LogRecord record) {
            int julLevelValue = record.getLevel().intValue();
            int slf4jLevel;
    
            if (julLevelValue <= TRACE_LEVEL_THRESHOLD) {
                slf4jLevel = LocationAwareLogger.TRACE_INT;
            } else if (julLevelValue <= DEBUG_LEVEL_THRESHOLD) {
                slf4jLevel = LocationAwareLogger.DEBUG_INT;
            } else if (julLevelValue <= INFO_LEVEL_THRESHOLD) {
                slf4jLevel = LocationAwareLogger.INFO_INT;
            } else if (julLevelValue <= WARN_LEVEL_THRESHOLD) {
                slf4jLevel = LocationAwareLogger.WARN_INT;
            } else {
                slf4jLevel = LocationAwareLogger.ERROR_INT;
            }
            String i18nMessage = getMessageI18N(record);
            lal.log(null, java.util.logging.Logger.class.getName(), slf4jLevel, i18nMessage, null,
                    record.getThrown());
        }
    
        @Override
        public void flush() {
            // TODO Auto-generated method stub
    
        }
    
        @Override
        public void close() throws SecurityException {
            // TODO Auto-generated method stub
    
        }
    
    }
    复制代码

    3. 集成代码

    复制代码
    public class JulRouter {
        private static String loggerName = JulRouter.class.getPackage().getName();
        private static Logger logger = Logger.getLogger(loggerName);
        private static void writeLogs() {
            logger.warning("this the warining message");
            logger.severe("this the severe message");
            logger.info("this the info message");
            logger.finest("this the finest message");
        }
        public static void main(String[] args) {
            Thread.currentThread().setName("JUL-Thread");
            JulLoggerWrapper wrapper = new JulLoggerWrapper(loggerName);
            wrapper.updateLevel(LoggerLevel.DEBUG);
            System.out.println("slf4j print===========");
            wrapper.install();
            writeLogs();
            System.out.println("jul print===========");
            wrapper.uninstall();
            writeLogs();
        }
    }
    复制代码

    4. log4j,properties 配置

    采用slf4j + log4j的方案,在classpath中设置log4j.properties即可

    复制代码
    log4j.rootLogger=INFO, console
    # simple console log
    log4j.appender.console=org.apache.log4j.ConsoleAppender
    log4j.appender.console.layout=org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss}] %p ~ %m%n
    ## for jul logging
    log4j.logger.org.zales.dmo.samples.logging=TRACE,julAppender
    log4j.additivity.org.zales.dmo.samples.logging=false
    log4j.appender.julAppender=org.apache.log4j.ConsoleAppender
    log4j.appender.julAppender.layout=org.apache.log4j.PatternLayout
    log4j.appender.julAppender.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss}]--[%t] [%p] -%l - %m%n
    复制代码
  • 相关阅读:
    Delphi 正则表达式之TPerlRegEx 类的属性与方法(3): Start、Stop
    Delphi 正则表达式之TPerlRegEx 类的属性与方法(2): 关于子表达式
    Delphi 正则表达式之TPerlRegEx 类的属性与方法(1): 查找
    Delphi 正则表达式语法(10): 选项
    Delphi 正则表达式语法(9): 临界匹配
    Delphi 正则表达式语法(8): 引用子表达式
    Delphi 正则表达式语法(7): 匹配转义字符
    看阿里P7讲MyBatis:从MyBatis的理解以及配置和实现全帮你搞懂
    公司新来的小姐姐不懂java中的static关键字,这样给她描述不香吗?
    2020阿里,字节跳动,JAVA岗(一线企业校招、社招)面试题合集
  • 原文地址:https://www.cnblogs.com/chenliyang/p/6543670.html
Copyright © 2011-2022 走看看