简介

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

架构介绍

  • Loggers:被称为记录器,应用程序通过获取Logger对象,调用其API来发布日志信息。Logger通常时应用程序访问日志系统的入口程序。
  • Appenders:也被称为Handlers,每个Logger都会关联一组Handlers,Logger会将日志交给关联Handlers处理,由Handlers负责将日志做记录。Handlers在此是一个抽象,其具体的实现决定了日志记录的位置可以是控制台、文件、网络上的其他日志服务或操作系统日志等。
  • Layouts:也被称为Formatters,它负责对日志事件中的数据进行转换和格式化。Layouts决定了数据在一条日志记录中的最终形式。
  • Level:每条日志消息都有一个关联的日志级别。该级别粗略指导了日志消息的重要性和紧迫,我可以将Level和Loggers,Appenders做关联以便于我们过滤消息。
  • Filters:过滤器,根据需要定制哪些信息会被记录,哪些信息会被放过。

    用户使用Logger来进行日志记录,Logger持有若干个Handler,日志的输出操作是由Handler完成的。在Handler在输出日志前,会经过Filter的过滤,判断哪些日志级别过滤放行哪些拦截,Handler会将日志内容输出到指定位置(日志文件、控制台等)。Handler在输出日志时会使用Layout,将输出内容进行排版。

1
2
3
4
5
6
7
8
9
@Test
public void test01(){
Logger logger = Logger.getLogger("cn.quguai.JULTest");
logger.info("Hello JUL");

logger.log(Level.INFO, "info msg");

logger.log(Level.INFO, "message:{0}:{1}", new Object[]{"quguai", 1});
}

日志级别

  • java.util.logging.Level中定义了日志的级别:
    • SEVERE(最高值)
    • WARNING
    • INFO (默认级别)
    • CONFIG
    • FINE
    • FINER
    • FINEST(最低值)
  • 还有两个特殊的级别:
    • OFF,可用来关闭日志记录。
    • ALL,启用所有消息的日志记录。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      @Test
      public void testLevel(){
      Logger logger = Logger.getLogger("cn.quguai.JULTest");
      logger.severe("severe");
      logger.warning("warning");
      logger.info("info");
      logger.config("config");
      logger.fine("fine");
      logger.finer("finer");
      logger.finest("finest");
      }

自定义日志级别配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Test
public void testSetLevel() throws IOException {
Logger logger = Logger.getLogger("cn.quguai.JULTest");

// 关闭原有处理器
logger.setUseParentHandlers(false);

// 创建ConsoleHandle 和 SimpleFormatter 格式化器
ConsoleHandler consoleHandler = new ConsoleHandler();
SimpleFormatter simpleFormatter = new SimpleFormatter();

// 进行关联
logger.addHandler(consoleHandler);
consoleHandler.setFormatter(simpleFormatter); // 默认就是SimpleFormatter

// 创建FileHandle
FileHandler fileHandler = new FileHandler("./jul.log");

// 进行关联
logger.addHandler(fileHandler);
fileHandler.setFormatter(simpleFormatter);

// 设置默认级别
consoleHandler.setLevel(Level.ALL);
logger.setLevel(Level.ALL);
// fileHandler.setLevel(Level.ALL); 默认沿用logger的配置

logger.severe("severe");
logger.warning("warning");
logger.info("info");
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
}

Logger父子关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test
public void testLogParent(){
Logger logger1 = Logger.getLogger("cn.quguai");
Logger logger2 = Logger.getLogger("cn");
// 默认顶级Logger对象是 java.util.logging.LogManager$RootLogger
System.out.println(logger2.getParent());

// 修改logger2的配置
logger2.setUseParentHandlers(false);
ConsoleHandler consoleHandler = new ConsoleHandler();
logger2.addHandler(consoleHandler);

logger2.setLevel(Level.ALL);
consoleHandler.setLevel(Level.ALL);

logger1.severe("severe");
logger1.warning("warning");
logger1.info("info");
logger1.config("config");
logger1.fine("fine");
logger1.finer("finer");
logger1.finest("finest");
}

Logger默认配置文件

文件路径:D:\jdk-11.0.10\conf\logging.properties

1
2
3
4
5
6
7
java.util.logging.Logger#getLogger() ->
java.util.logging.Logger#demandLogger ->
java.util.logging.LogManager#getLogManager ->
java.util.logging.LogManager#ensureLogManagerInitialized->
java.util.logging.LogManager#readPrimordialConfiguration ->
java.util.logging.LogManager#readConfiguration() ->
java.util.logging.LogManager#getConfigurationFileName
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
############################################################
# Global properties
############################################################

# "handlers" specifies a comma separated list of log Handler
# classes. These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler

# To also add the FileHandler, use the following line instead.
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers. For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = %h/java%u.log # 当前用户目录下 java01.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
# Default number of locks FileHandler can obtain synchronously.
# This specifies maximum number of attempts to obtain lock file by FileHandler
# implemented by incrementing the unique field %u as per FileHandler API documentation.
java.util.logging.FileHandler.maxLocks = 100
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

# Example to customize the SimpleFormatter output format
# to print one-line log message like this:
# <level>: <log message> [<date/time>]
#
# java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n

############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################

# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
com.xyz.foo.level = SEVERE

自定义配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 顶级父元素的默认的处理器
handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler // 追加文件管理器

# 顶级父元素RootLogger的默认输出级别
.level= ALL // 修改默认级别
# 文件配置路径
java.util.logging.FileHandler.pattern = ./java%u.log
# 指定日志文件内容大小
java.util.logging.FileHandler.limit = 50000
# 指定日志文件数量
java.util.logging.FileHandler.count = 1

java.util.logging.FileHandler.maxLocks = 100
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter #XML格式化器
java.util.logging.FileHandler.append = true

java.util.logging.ConsoleHandler.level = ALL # 修改默认级别
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.ConsoleHandler.encoding = UTF-8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testProperties() throws IOException {
InputStream ins = JULTest.class.getClassLoader().getResourceAsStream("logging.properties");

// LogManager 是一个单例对象
LogManager logManager = LogManager.getLogManager();
logManager.readConfiguration(ins);

Logger logger = Logger.getLogger("cn.quguai.JULTest");
logger.severe("severe");
logger.warning("warning");
logger.info("info");
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
ins.close();
}

JUL配置文件详情

ConsoleHandler配置形式

java.util.logging.ConsoleHandler.encoding = UTF-8

image.png

配置文件格式化方式

java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n

SurrogateLogger.getSimpleFormat 获取配置文件中的key值对应的格式化方式
image.png

追加方式

java.util.logging.FileHandler.append = true

自定义Logger

1
2
3
4
# 自定义Logger
cn.quguai.handlers = java.util.logging.FileHandler
cn.quguai.level = CONFIG
cn.quguai.useParentHandlers=false

日志原理解析

  1. 初始化LogManager
    1. LogManager加载logging.properties配置
    2. 添加Logger到LogManager
  2. 从单例LogManager获取Logger
  3. 设置级别Level,并指定日志记录LogRecord
  4. Filter提供了日志级别之外更细粒度的控制
  5. Handler是用来处理日志输出位置
  6. Formatter是用来格式化LogRecord的

image.png