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

    参考资料

    Java Util Logging 组件介绍 
    https://www.loggly.com/ultimate-guide/java-logging-basics/ 
    Jul API Turturial 
    http://www.vogella.com/tutorials/Logging/article.html 
    Log4j -Jul 适配器组件 
    https://logging.apache.org/log4j/2.0/log4j-jul/

  • 相关阅读:
    [LeetCode]2. Add Two Numbers链表相加
    Integration between Dynamics 365 and Dynamics 365 Finance and Operation
    向视图列添加自定义图标和提示信息 -- PowerApps / Dynamics365
    Update the Power Apps portals solution
    Migrate portal configuration
    Use variable to setup related components visible
    Loyalty management on Retail of Dynamic 365
    Modern Fluent UI controls in Power Apps
    Change screen size and orientation of a canvas app in Power App
    Communication Plan for Power Platform
  • 原文地址:https://www.cnblogs.com/littleatp/p/6354441.html
Copyright © 2011-2022 走看看