• java.util.logging.Logger日志生成过程浅析 (转)


    http://www.tuicool.com/articles/vy6Zrye

    ******************************************

    java.util.logging包中主要的类有以下几个:

    LogManager    存在一个单一的全局 LogManager 对象,它可用于维护 Logger 和日志服务的一组共享状态。
    Logger        Logger 对象用来记录特定系统或应用程序组件的日志消息
    LogRecord     LogRecord 对象用于在日志框架和单个日志 Handler 之间传递日志请求
    Handler       Handler 对象从 Logger 中获取日志信息,并将这些信息导出
    Formatter     Formatter 为格式化 LogRecords 提供支持

    简单的来说:

    LogManager是一个存放Logger的工厂,我们每次生成Logger时,如果是新的Logger(Logger的名字在LoggerManager中不存在),那么将在LogManager中生成一个新的Logger,如果是已有的Logger,那么直接取之并使用。 

    Logger logDemo = Logger.getLogger("log.demo");    // 在Logger工厂(LogManager)中生成一个名为log.demo的日志管理器,

    当使用Logger来生成一条日志时,其将首先将日志保存在LogRecord中,并将LogRecord传送给Handler进行导出工作,当然如果需要对导出内容进行格式化处理,那么就需要在Handler导出日志前,为Handler指定一个日志格式化处理器,也就是Formatter干的工作。

    logDemo.severe("this is a severe log");    // 记录一条日志级别为Level.SEVERE的日志消息

    运行程序将看到控制台输入消息:

    五月 24, 2015 7:44:10 上午 cn.kolbe.java.log.LogDemo main
    严重: this is a severe log

    在这有个疑问,前面说过LogRecord负责记录消息,Handler负责日志导出,Formatter负责日志格式化,可是为何上面我们并没有显示将消息传递给LogRecord,也没有显示为Logger添加Handler,更没有为Handler添加Formatter,其就自动的在控制台(System.err)中输出消息了!为了解除疑问,让我们看一下Logger的源代码中的severe()方法

     1 public class Logger {
     2     public void severe(String msg) {
     3         log(Level.SEVERE, msg);
     4     }
     5     public void log(Level level, String msg) {
     6         if (!isLoggable(level)) {
     7             return;
     8         }
     9         LogRecord lr = new LogRecord(level, msg);
    10         doLog(lr);
    11     }
    12     private void doLog(LogRecord lr) {
    13         lr.setLoggerName(name);
    14         final LoggerBundle lb = getEffectiveLoggerBundle();
    15         final ResourceBundle  bundle = lb.userBundle;
    16         final String ebname = lb.resourceBundleName;
    17         if (ebname != null && bundle != null) {
    18             lr.setResourceBundleName(ebname);
    19             lr.setResourceBundle(bundle);
    20         }
    21         log(lr);
    22     }
    23     public void log(LogRecord record) {
    24         if (!isLoggable(record.getLevel())) {
    25             return;
    26         }
    27         Filter theFilter = filter;
    28         if (theFilter != null && !theFilter.isLoggable(record)) {
    29             return;
    30         }
    31 
    32         // Post the LogRecord to all our Handlers, and then to
    33         // our parents' handlers, all the way up the tree.
    34 
    35         Logger logger = this;
    36         while (logger != null) {
    37             final Handler[] loggerHandlers = isSystemLogger
    38                 ? logger.accessCheckedHandlers()
    39                 : logger.getHandlers();
    40 
    41             for (Handler handler : loggerHandlers) {
    42                 handler.publish(record);
    43             }
    44 
    45             final boolean useParentHdls = isSystemLogger
    46                 ? logger.useParentHandlers
    47                 : logger.getUseParentHandlers();
    48 
    49             if (!useParentHdls) {
    50                 break;
    51             }
    52 
    53             logger = isSystemLogger ? logger.parent : logger.getParent();
    54         }
    55     }
    56 }

    从源码中可以看到

    1)severe()方法中调用了public void log(Level level, String msg)方法,将日志消息级别设置为Level.SEVERE,

    2)在log(Level level, String msg)方法中可以看到,其生成了一个 LogRecord 对象,也就是日志消息对象

    3)接着又调用了private void doLog(LogRecord lr)方法,该方法主要是设置本地化语言包,暂且跳过

    4)最后调用public void log(LogRecord record)方法,在该方法中有个while循环,首先通过Logger类中的getHandlers()方法,获取当前Logger对象中已经设置的Handler对象,然后调用Handler类中的publish()方法打印日志消息记录 (可是我们并没有设置Handler啊?接着往下看)

    5)在循环体底下可以看到logger = isSystemLogger ? logger.parent : logger.getParent();该语句的作用是遍历logger对象中的父对象,并将LogRecord传递给父对象的Handler

    6)

    Logger parent = logDemo.getParent();
    System.out.println("parent.getName() : " + parent.getName());
    System.out.println("parent.getClass().getName() : " + parent.getClass().getName());

    控制台输入:

    parent.getName() : 
    parent.getClass().getName() : java.util.logging.LogManager$RootLogger

    发现Logger的默认根日志对象是LogManager的内部类RootLogger类的对象,并且根日志对象的名字为空白字符!

    让我们验证一下:

    Logger rootLog = Logger.getLogger("");
    System.out.println("rootLog.getClass().getName() : " + rootLog.getClass().getName());

    控制台输出:

    rootLog.getClass().getName() : java.util.logging.LogManager$RootLogger 

    另外让我们再看一下根Log是否有默认的Handler对象:

    Handler[] handlers = rootLog.getHandlers();
    for(int i = 0; i < handlers.length; i++) {
      System.out.println(handlers[i].getClass().getName());
    }

    控制台输出:

    java.util.logging.ConsoleHandler

    原来根Log默认装配了ConsoleHandler(对应的是控制台输出)所以在我们生成日志消息时没有显示指定Handler时,其也能在控制台中输入日志消息,你可能会问,根Log是在什么时候生成的?其是在我们应用程序中第一次调用Logger.getLogger("log name")时由LogManager对象进行装配的。具体过程将在下次进行解析。

    注:另外文中忽略了Level(Log日志级别):用来控制日志发布的级别,以及Filter(Log日志过滤):用来鉴定消息是否应发布给LogRecord或者直接丢弃。这块内容将找个时间补上。

  • 相关阅读:
    从零到一,使用实时音视频 SDK 一起开发一款 Zoom 吧
    七牛云助你度寒冬 | 每天 10:24, 新用户抢全额免单
    (下)挖掘传统行业日志大数据的无限价值
    (上)挖掘传统行业日志大数据的无限价值
    8.27 直播| 挖掘传统行业日志大数据的无限价值
    千亿级数量下日志分析系统的技术架构选型
    七牛云工程效率部测试服务如何为 70 万+ 客户保驾护航?
    云计算的新墨菲定律
    如何建设高吞吐量的日志平台
    新一代智能视频云发展现状分析:五大要素成关键
  • 原文地址:https://www.cnblogs.com/zhao1949/p/5952361.html
Copyright © 2020-2023  润新知