本章节源代码

https://github.com/gitaxin/JavaLog/blob/master/JUL/src/test/java/cn/giteasy/jul/test/JULTest.java

JUL简介

JUL 全称 Java Util Logging,它是java原生的日志框架,在java.util.logging包下,使用时不需要另外引用第三方的类库,相对其他的框架使用方便,学习简单,主要是使用在小型应用中。

JUL组件介绍

二、JUL - 图1

  • Logger:

被称为记录器,应用程序通过获取 Logger 对象,调用其 API 来发布日志信息。Logger通常被认为是访问日志系统的入口程序。

  • Handler:

处理器,每个 Logger 都会关联一个或者是一组 Handler,Logger 会将日志交给关联的Handler 去做处理,由 Handler 负责将日志做记录。Handler 具体实现了日志的输出位置,比如可以输出到控制台或者是文件中等等。

  • Filter:

过滤器,根据需要定制哪些信息会被记录,哪些信息会被略过。

  • Formatter:格式化组件,它负责对日志中的数据和信息进行转换和格式化,所以它决定了我们输出日志最终的形式。。
  • Level:日志的输出级别,每条日志消息都有一个关联的级别。我们根据输出级别的设置,用来展现最终所呈现的日志信息。根据不同的需求,设置不同的级别。

    案例

    引入依赖

    1. <!-- 使用Junit进行测试 -->
    2. <dependency>
    3. <groupId>junit</groupId>
    4. <artifactId>junit</artifactId>
    5. <version>4.13.2</version>
    6. <scope>test</scope>
    7. </dependency>

入门

  1. @Test
  2. public void testJUL(){
  3. //日志入口:java.util.logging.Logger
  4. //Logger对象的创建方式,不能直接new对象
  5. //取得对象的方法参数,需要引入当前类的全路径字符串(Logger是有父子关系的,根据包的结构(后面有介绍))
  6. Logger logger = Logger.getLogger("cn.giteasy.jul.test.JULTest");
  7. /*
  8. *对于日志的输出,有两种方式
  9. *
  10. * 方式一:
  11. * 直调用日志级别相关的方法,方法中传递日志输出的信息
  12. */
  13. logger.info("INFO>>>>>>>>>>>>>>>>>>>>>>>>>>");
  14. System.out.println("=====================================================");
  15. /*
  16. 方式二:
  17. 调用log方法,然后通过传入Level类型的的级别来定义日志的级别,并传入日志输出的信息参数
  18. */
  19. //JUL默认级别是INFO
  20. logger.log(Level.OFF,"OFF>>>>>>>>>>>>>>>>>>>>>>>>>>");
  21. logger.log(Level.SEVERE,"SEVERE>>>>>>>>>>>>>>>>>>>>>>>>>>");
  22. logger.log(Level.WARNING,"WARNING>>>>>>>>>>>>>>>>>>>>>>>>>>");
  23. logger.log(Level.INFO,"INFO>>>>>>>>>>>>>>>>>>>>>>>>>>");
  24. logger.log(Level.CONFIG,"CONFIG>>>>>>>>>>>>>>>>>>>>>>>>>>");
  25. logger.log(Level.FINE,"FINE>>>>>>>>>>>>>>>>>>>>>>>>>>");
  26. logger.log(Level.FINER,"FINER>>>>>>>>>>>>>>>>>>>>>>>>>>");
  27. logger.log(Level.FINEST,"FINEST>>>>>>>>>>>>>>>>>>>>>>>>>>");
  28. logger.log(Level.ALL,"ALL>>>>>>>>>>>>>>>>>>>>>>>>>>");
  29. System.out.println("=====================================================");
  30. /*
  31. 输出日志时,传入系统运行中的数据
  32. */
  33. String name = "giteasy";
  34. String like = "Java";
  35. logger.log(Level.INFO,"My name is {0}, my like {1}",new Object[]{name,like});
  36. /**
  37. * 输出结果:
  38. *
  39. * 十二月 27, 2021 11:10:22 下午 cn.giteasy.jul.test.JULTest testJUL
  40. * 信息: My name is giteasy, my like Java
  41. */
  42. }

JUL日志级别

日志的级别(可通过源码查看)java.util.logging.Level

OFF Integer.MAX_VALUE
SEVERE 错误 1000 最高级的日志级别
WARNING 警告 900
INFO (默认级别)消息 800
CONFIG 配置 700
FINE 详细信息(少) 500
FINER 详细信息(中) 400
FINEST 详细信息(多) 300 最低级的日志级别
ALL Integer.MIN_VALUE

两个特殊的级别
OFF 可用来关闭日志记录 Integer.MAX_VALUE 整型最大值
ALL 启用所有消息的日志记录 Integer.MIN_VALUE 整型最小值

假如我们设定的日志级别是INFO(800),那么在打印的时候,我们在应用中打印的日志的时候以下的日志都会输出

  1. SEVERE 1000
  2. WARNING 900
  3. INFO 800
  4. 这是因为他们的值,比我们设定的值INFO800)大或相等。
  5. 例如: logger.info("我是 SEVERE 信息");
  6. logger.info("我是 WARNING 信息");
  7. logger.info("我是 INFO 信息");
  8. logger.info("我是 CONFIG 信息"); //不打印,因为比我们设定的INFO级别小。

如果你想要自定义级别,仅仅只是通过setLevel()设置日志级别,是不起作用的,需要搭配处理器handler共同设置才可以

  1. Logger logger = Logger.getLogger("cn.giteasy.jul.test.JULTest");
  2. //是不起作用的,需要搭配处理器handler共同设置才可以,查看下节
  3. //logger.setLevel(Level.FINE);
  4. logger.log(Level.SEVERE,"SEVERE>>>>>>>>>>>>>>>>>>>>>>>>>>");
  5. logger.log(Level.WARNING,"WARNING>>>>>>>>>>>>>>>>>>>>>>>>>>");
  6. logger.log(Level.INFO,"INFO>>>>>>>>>>>>>>>>>>>>>>>>>>");
  7. logger.log(Level.CONFIG,"CONFIG>>>>>>>>>>>>>>>>>>>>>>>>>>");
  8. logger.log(Level.FINE,"FINE>>>>>>>>>>>>>>>>>>>>>>>>>>");
  9. logger.log(Level.FINER,"FINER>>>>>>>>>>>>>>>>>>>>>>>>>>");
  10. logger.log(Level.FINEST,"FINEST>>>>>>>>>>>>>>>>>>>>>>>>>>");
  11. logger.log(Level.ALL,"ALL>>>>>>>>>>>>>>>>>>>>>>>>>>");

自定义日志级别

  1. /**
  2. * 自定义日志级别演示
  3. */
  4. @Test
  5. public void testSetDefaultLevel(){
  6. //日志记录器
  7. Logger logger = Logger.getLogger("cn.giteasy.jul.test.JULTest");
  8. //将默认的日志打印方式关掉
  9. //参数设置为false ,打印日志时就不会按照默认的方式去打印了
  10. logger.setUseParentHandlers(false);
  11. //日志处理器:日志处理器有控制台处理器、文件日志处理器等等,这里演示控制台日志处理器
  12. ConsoleHandler consoleHandler = new ConsoleHandler();
  13. //设置输出格式
  14. SimpleFormatter simpleFormatter = new SimpleFormatter();
  15. consoleHandler.setFormatter(simpleFormatter);
  16. //将处理器添加到日志记录器
  17. logger.addHandler(consoleHandler);
  18. //设置日志的打印级别
  19. //日志记录器和处理器的级别均需要进行统一的设置,才可以达到日志级别自定义设置的需求
  20. logger.setLevel(Level.FINE);
  21. consoleHandler.setLevel(Level.FINE);
  22. logger.log(Level.SEVERE,"SEVERE>>>>>>>>>>>>>>>>>>>>>>>>>>");
  23. logger.log(Level.WARNING,"WARNING>>>>>>>>>>>>>>>>>>>>>>>>>>");
  24. logger.log(Level.INFO,"INFO>>>>>>>>>>>>>>>>>>>>>>>>>>");
  25. logger.log(Level.CONFIG,"CONFIG>>>>>>>>>>>>>>>>>>>>>>>>>>");
  26. logger.log(Level.FINE,"FINE>>>>>>>>>>>>>>>>>>>>>>>>>>");
  27. logger.log(Level.FINER,"FINER>>>>>>>>>>>>>>>>>>>>>>>>>>");
  28. logger.log(Level.FINEST,"FINEST>>>>>>>>>>>>>>>>>>>>>>>>>>");
  29. logger.log(Level.ALL,"ALL>>>>>>>>>>>>>>>>>>>>>>>>>>");
  30. /**
  31. * 控制台打印结果
  32. *
  33. * 一月 15, 2022 4:17:38 下午 cn.giteasy.jul.test.JULTest testSetDefaultLevel
  34. * 严重: SEVERE>>>>>>>>>>>>>>>>>>>>>>>>>>
  35. * 一月 15, 2022 4:17:38 下午 cn.giteasy.jul.test.JULTest testSetDefaultLevel
  36. * 警告: WARNING>>>>>>>>>>>>>>>>>>>>>>>>>>
  37. * 一月 15, 2022 4:17:38 下午 cn.giteasy.jul.test.JULTest testSetDefaultLevel
  38. * 信息: INFO>>>>>>>>>>>>>>>>>>>>>>>>>>
  39. * 一月 15, 2022 4:17:38 下午 cn.giteasy.jul.test.JULTest testSetDefaultLevel
  40. * 配置: CONFIG>>>>>>>>>>>>>>>>>>>>>>>>>>
  41. * 一月 15, 2022 4:17:38 下午 cn.giteasy.jul.test.JULTest testSetDefaultLevel
  42. * 详细: FINE>>>>>>>>>>>>>>>>>>>>>>>>>>
  43. */
  44. }

将日志输出到文件中

  1. @Test
  2. public void testFileLog() throws IOException {
  3. //日志记录器
  4. Logger logger = Logger.getLogger("cn.giteasy.jul.test.JULTest");
  5. logger.setUseParentHandlers(false);
  6. //文件日志处理器
  7. FileHandler fileHandler = new FileHandler("d:\\jul_test.log");
  8. SimpleFormatter simpleFormatter = new SimpleFormatter();
  9. fileHandler.setFormatter(simpleFormatter);
  10. //将处理器添加到日志记录器
  11. logger.addHandler(fileHandler);
  12. logger.setLevel(Level.ALL);
  13. fileHandler.setLevel(Level.ALL);
  14. logger.log(Level.SEVERE,"SEVERE>>>>>>>>>>>>>>>>>>>>>>>>>>");
  15. logger.log(Level.WARNING,"WARNING>>>>>>>>>>>>>>>>>>>>>>>>>>");
  16. logger.log(Level.INFO,"INFO>>>>>>>>>>>>>>>>>>>>>>>>>>");
  17. logger.log(Level.CONFIG,"CONFIG>>>>>>>>>>>>>>>>>>>>>>>>>>");
  18. logger.log(Level.FINE,"FINE>>>>>>>>>>>>>>>>>>>>>>>>>>");
  19. logger.log(Level.FINER,"FINER>>>>>>>>>>>>>>>>>>>>>>>>>>");
  20. logger.log(Level.FINEST,"FINEST>>>>>>>>>>>>>>>>>>>>>>>>>>");
  21. logger.log(Level.ALL,"ALL>>>>>>>>>>>>>>>>>>>>>>>>>>");
  22. }

将文件同时输出到控制台与文件中

  1. /**
  2. *文件日志与控制台日志同时打印
  3. */
  4. @Test
  5. public void testConsoleAndFileLog() throws IOException {
  6. //日志记录器
  7. Logger logger = Logger.getLogger("cn.giteasy.jul.test.JULTest");
  8. logger.setUseParentHandlers(false);
  9. //文件日志处理器
  10. FileHandler fileHandler = new FileHandler("d:\\jul_test.log");
  11. //控制台日志处理器
  12. ConsoleHandler consoleHandler = new ConsoleHandler();
  13. SimpleFormatter simpleFormatter = new SimpleFormatter();
  14. fileHandler.setFormatter(simpleFormatter);
  15. consoleHandler.setFormatter(simpleFormatter);
  16. //将处理器添加到日志记录器
  17. logger.addHandler(fileHandler);
  18. logger.addHandler(consoleHandler);
  19. logger.setLevel(Level.ALL);
  20. fileHandler.setLevel(Level.ALL);//文件打印日志级别
  21. consoleHandler.setLevel(Level.CONFIG);//控制台日志打印级别
  22. logger.log(Level.SEVERE,"SEVERE>>>>>>>>>>>>>>>>>>>>>>>>>>");
  23. logger.log(Level.WARNING,"WARNING>>>>>>>>>>>>>>>>>>>>>>>>>>");
  24. logger.log(Level.INFO,"INFO>>>>>>>>>>>>>>>>>>>>>>>>>>");
  25. logger.log(Level.CONFIG,"CONFIG>>>>>>>>>>>>>>>>>>>>>>>>>>");
  26. logger.log(Level.FINE,"FINE>>>>>>>>>>>>>>>>>>>>>>>>>>");
  27. logger.log(Level.FINER,"FINER>>>>>>>>>>>>>>>>>>>>>>>>>>");
  28. logger.log(Level.FINEST,"FINEST>>>>>>>>>>>>>>>>>>>>>>>>>>");
  29. logger.log(Level.ALL,"ALL>>>>>>>>>>>>>>>>>>>>>>>>>>");
  30. }
  1. <br /> 用户使用Logger来进行日志的记录,Logger可以持有多个处理器Handler<br /> (日志的记录使用的是Logger,日志的输出使用的是Handler)<br /> 添加了哪些handler对象,就相当于需要根据所添加的handler将日志输出到指定的位置上,例如控制台、文件.

Logger之间的父子关系

  1. /**
  2. * 根据包的结构
  3. * logger是logger2 的父logger
  4. * logger2是logger3是父logger
  5. */
  6. Logger logger1 = Logger.getLogger("cn.giteasy");
  7. Logger logger2 = Logger.getLogger("cn.giteasy.jul");
  8. Logger logger3 = Logger.getLogger("cn.giteasy.jul.test");
  9. //获取logger2的父logger
  10. Logger parentLogger = logger2.getParent();
  11. System.out.println(parentLogger == logger1); //true


JUL中Logger之间是存在”父子”关系的

注意:这种父子关系不是我们普遍认为的类之间的继承关系

关系是通过树状结构存储的(目录、包结构)
JUL在初始化时会创建一个顶层RootLogger作为所有的Logger的父Logger
源码:

  1. java.util.logging.LogManager#ensureLogManagerInitialized();
  2. // Create and retain Logger for the root of the namespace.
  3. owner.rootLogger = owner.new RootLogger();
  4. //RootLogger是LogManager的内部类
  5. //owner:就是RootLogger的实例
  6. // java.util.logging.LogManager$RootLogger //默认的名称为 空串
  1. 以上的RootLogger对象作为树状结构的根节点存在的<br /> 将来自定义的父子关系通过路径来进行关联父子关系,同时也是节点之间的挂载关系

父Logger所做的设置,也同样作用于子Logger

  1. // 对logger1做打印设置
  2. logger1.setUseParentHandlers(false);
  3. //控制台日志处理器
  4. ConsoleHandler consoleHandler = new ConsoleHandler();
  5. SimpleFormatter simpleFormatter = new SimpleFormatter();
  6. consoleHandler.setFormatter(simpleFormatter);
  7. logger1.addHandler(consoleHandler);
  8. consoleHandler.setLevel(Level.ALL);
  9. logger1.setLevel(Level.ALL);
  10. //使用logger2打印
  11. logger2.log(Level.SEVERE,"SEVERE>>>>>>>>>>>>>>>>>>>>>>>>>>");
  12. logger2.log(Level.WARNING,"WARNING>>>>>>>>>>>>>>>>>>>>>>>>>>");
  13. logger2.log(Level.INFO,"INFO>>>>>>>>>>>>>>>>>>>>>>>>>>");
  14. logger2.log(Level.CONFIG,"CONFIG>>>>>>>>>>>>>>>>>>>>>>>>>>");
  15. logger2.log(Level.FINE,"FINE>>>>>>>>>>>>>>>>>>>>>>>>>>");
  16. logger2.log(Level.FINER,"FINER>>>>>>>>>>>>>>>>>>>>>>>>>>");
  17. logger2.log(Level.FINEST,"FINEST>>>>>>>>>>>>>>>>>>>>>>>>>>");
  18. logger2.log(Level.ALL,"ALL>>>>>>>>>>>>>>>>>>>>>>>>>>");
  19. //查看控制台打印结果,可知对父logger设置的打印参数,也会作用于子Logger

JUL配置文件

前面所有配置相关的操作,都是以java硬编码的形式进行的,更加专业的一种做法,就是使用配置文件,如果我们没有自己添加配置文件,则会使用系统默认的配置文件
查看源码可知,配置文件的位置

  1. owner.readPrimordialConfiguration();
  2. readConfiguration();

java.home —> 找到jre文件夹 —> lib —> logging.properties

  1. $JAVA_HOME\jre\lib\logging.properties

配置文件详解logging.properties
  1. # JUL 日志配置文件祥解
  2. # 默认配置文件位置:$JAVA_HOME\jre\lib\logging.properties
  3. #RootLogger使用的处理器,在获取RootLogger对象时进行的设置
  4. #默认的情况下,下述配置的是控制台的处理器,只能往控制台上进行输出操作
  5. #如果想要添加其他的处理器,在当前处理器类后面通过以逗号的形式进行分隔,可以添加多个处理器
  6. handlers= java.util.logging.ConsoleHandler
  7. #RootLogger的日志级别
  8. #默认的情况下,这是全局的日志级别,如果不手动配置其他的日志级别
  9. #则默认输出下述配置的级别以及更高的级别
  10. .level= CONFIG
  11. # 输出日志文件的路径
  12. # %h:用户目录
  13. # %u:序号,从0开始
  14. java.util.logging.FileHandler.pattern = %h/java%u.log
  15. #输出日志文件的限制(50000字节)
  16. java.util.logging.FileHandler.limit = 50000
  17. #设置日志文件的数量
  18. java.util.logging.FileHandler.count = 1
  19. #输出日志的格式
  20. #默认是以XML的方式进行的输出
  21. java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
  22. #控制台处理器属性设置
  23. #控制台输出默认的级别
  24. java.util.logging.ConsoleHandler.level = CONFIG
  25. #控制台默认输出的格式
  26. java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
  27. #也可以将日志级别设定到具体的某个包下
  28. #com.xyz.foo.level = SEVERE

自定义配置文件及自定义Logger

mylogger.properties

  1. # mylogger.properties
  2. handlers = java.util.logging.ConsoleHandler
  3. .level = CONFIG
  4. # 将日志输出到文件中
  5. # %h:用户目录
  6. # %u:序号,从0开始
  7. java.util.logging.FileHandler.pattern = %h/java%u.log
  8. java.util.logging.FileHandler.limit = 50000
  9. java.util.logging.FileHandler.count = 1
  10. #使用SimpleFormatter格式
  11. java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
  12. #以追加的方式输出日志,否则会覆盖掉以前的日志
  13. java.util.logging.FileHandler.append = true
  14. java.util.logging.ConsoleHandler.level = CONFIG
  15. java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
  16. #自定义Logger
  17. cn.giteasy.jul.handlers = java.util.logging.FileHandler
  18. #自定义logger级别
  19. cn.giteasy.jul.level = CONFIG
  20. #屏蔽父logger的日志设置
  21. cn.giteasy.jul.useParentHandlers = false
  1. @Test
  2. public void testMyLoggerForConfigFile() throws IOException {
  3. //读取自定义配置文件
  4. String path = JULTest.class.getClass().getResource("/mylogger.properties").getFile().toString();
  5. InputStream is = new FileInputStream(path);
  6. LogManager logManager = LogManager.getLogManager();
  7. logManager.readConfiguration(is);
  8. Logger logger = Logger.getLogger("cn.giteasy.jul.test");
  9. //使用logger2打印
  10. logger.log(Level.SEVERE,"SEVERE>>>>>>>>>>>>>>>>>>>>>>>>>>");
  11. logger.log(Level.WARNING,"WARNING>>>>>>>>>>>>>>>>>>>>>>>>>>");
  12. logger.log(Level.INFO,"INFO>>>>>>>>>>>>>>>>>>>>>>>>>>");
  13. logger.log(Level.CONFIG,"CONFIG>>>>>>>>>>>>>>>>>>>>>>>>>>");
  14. logger.log(Level.FINE,"FINE>>>>>>>>>>>>>>>>>>>>>>>>>>");
  15. logger.log(Level.FINER,"FINER>>>>>>>>>>>>>>>>>>>>>>>>>>");
  16. logger.log(Level.FINEST,"FINEST>>>>>>>>>>>>>>>>>>>>>>>>>>");
  17. logger.log(Level.ALL,"ALL>>>>>>>>>>>>>>>>>>>>>>>>>>");
  18. }

JUL日志使用总结(原理解析)

  1. 初始化LogManager

LogManager加载logging-properties配置文件
添加Logger到LogManager

  1. 从单例的LogManager获取Logger
  2. 设置日志级别Level,在打印的过程中使用到了日志记录的LogRecord类
  3. Filter作为过滤器提供了日志级别之外更细粒度的控制
  4. Handler日志处理器,决定日志的输出位置,例如控制台、文件…
  5. Formatter是用来格式化输出的,例如 xml格式、普通文本格式…