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

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

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

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

简单的来说:

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

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

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

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

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

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

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

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

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

控制台输入:

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

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

让我们验证一下:

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

控制台输出:

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

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

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

控制台输出:

  1. java.util.logging.ConsoleHandler

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

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

java.util.logging.Logger日志生成过程浅析 (转)的更多相关文章

  1. 通配置文件的方式控制java.util.logging.Logger日志输出

    转自:http://zochen.iteye.com/blog/616151 简单的实现了下利用JDK中类java.util.logging.Logger来记录日志.主要在于仿照log4j方式用配置文 ...

  2. 【java】java自带的java.util.logging.Logger日志功能

    偶然翻阅到一篇文章,注意到Java自带的Logger日志功能,特地来细细的看一看,记录一下. 1.Java自带的日志功能,默认的配置 ①Logger的默认配置,位置在JRE安装目录下lib中的logg ...

  3. Java日志工具之java.util.logging.Logger

    今天总结下JDK自带的日志工具Logger,虽然它一直默默无闻,但有时使用它却比较方便.更详细的信息可以查看JDK API手册,本文只是简单示例入门. 创建Logger 我们可以使用Logger的工厂 ...

  4. Java日志组件1---Jdk自带Logger(java.util.logging.Logger)

    最近在看日志的一些东西,发现利用JDK自带的log也可以简单的实现日志的输出,将日志写入文件的过程记录如下: 1.新建LogUtil.Java( 里面写了几个静态方法,为log设置等级.添加log控制 ...

  5. Java日志介绍(1)-java.util.logging.Logger

    java.util.logging.Logger是JDK自带的日志工具,其简单实现了日志的功能,不是很完善,所以在实际应用中使用的比较少.本文直接用代码演示其使用方法,文中所使用到的软件版本:Java ...

  6. java.util.logging.Logger 使用详解

    概述: 第1部分 创建Logger对象 第2部分 日志级别 第3部分 Handler 第4部分 Formatter 第5部分 自定义 第6部分 Logger的层次关系 参考 第1部分 创建Logger ...

  7. java.util.logging.Logger基础教程

    从JDK1.4开始即引入与日志相关的类java.util.logging.Logger,但由于Log4J的存在,一直未能广泛使用.综合网上各类说法,大致认为: (1)Logger:适用于小型系统,当日 ...

  8. java.util.logging.Logger基础

    1. 定义 java.util.logging.Logger是Java自带的日志类,可以记录程序运行中所产生的日志.通过查看所产生的日志文件,可以分析程序的运行状况,出现异常时,分析及定位异常. 2. ...

  9. 2.java.util.logging.Logger使用详解

    一.java.util.logging.Logger简介 java.util.logging.Logger不是什么新鲜东西了,1.4就有了,可是因为log4j的存在,这个logger一直沉默着, 其实 ...

随机推荐

  1. Hibernate学习笔记(十) — HQL查询

    一.HQL的一对多查询 班级(1)->(多)学生 /** * 实现Classes与Student的内连接 * * SELECT c.*,s.* * from classes c INNER JO ...

  2. 伪造A标签跳转(非window.open)Jquery

    尊重原创:http://blog.csdn.net/zdb330906531

  3. 〖Linux〗VirtualBox修改虚拟电脑硬盘(vdi)空间大小

    1. 查看需要修改的虚拟硬盘: [scue@Link:tftpserver]$ vboxmanage list hdds UUID: 79d65850--40c3-a8e7-715b199d1673 ...

  4. 在shell中使用sed命令替换/为\/

    sed命令相关: https://www.cnblogs.com/ggjucheng/archive/2013/01/13/2856901.html https://www.cnblogs.com/D ...

  5. Linux服务器修改时区时间

    时间的一致性很关键,对于日志的分析和程序的对接都至关重要! 01.tzselect 修改时区 可以使用命令 tzselect,修改时区.操作示例: $ tzselect Please identify ...

  6. HTTP头返回码分析

    http协议通讯时,在客户端发送请求后(request),服务器端返回的状态码解释(response) http状态码 1**:请求收到,继续处理 2**:操作成功收到,分析.接受           ...

  7. 理解ROC和AUC

    分类器各种各样,如何评价这些分类器的性能呢?(这里只考虑二元分类器,分类器的输出为概率值) 方法一:概率定义法 从正样本中随机选取元素记为x,从负样本中随机选取元素记为y,x的置信度大于y的概率 计算 ...

  8. jQuery+ajax中,让window.open不被拦截(转)

    方法1:<input type="button" class="preview" value="预览"/>$('.preview ...

  9. 用浏览器访问WCF

    在开发的时候,为客户端编写代码访问WCF的方式,我们应该比较熟悉,主要是添加一个代理,然后通过这个代理进行访问. 如果用浏览器访问WCF呢?(其实最终是想在JS里面访问)用浏览器访问.测试Web Se ...

  10. 关于Andorid的RecyclerView在V7包下找不到的解决办法

      关于Andorid的RecyclerView在V7包下找不到的解决办法 最近在学习使用RecyclerView替换现有的ListView,看了几篇文章.当准备自己动手实现的时候发现,V7包下找不到 ...