日志实现框架(2):JUL(Java Util Logging)

目录

JUL日志框架介绍

▎相关组件说明

创建使用Logger对象

日志级别

▎拓展:自定义日志级别

设置日志级别

Handler处理器类型

格式化程序(Formatter)

▎ SimpleFormatter

▎ XMLFormatter

Logger对象的父子关系

▎继承特性


日志框架出现的历史顺序:Log4j → JUL → JCL → slf4j → logback → log4j2

JUL日志框架介绍

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

通过Logger日志记录器进行日志的记录和输出,logger主要可以做两件事:

  1.   设置要输出的日志级别  
  2.   设置日志的具体内容

流程如下图所示

日志记录器底层调用了Handler处理器,它的主要功能是将日志进行记录、日志格式化的转换、以及输出到指定位置,比如说控制台、文件等等

那么Handler 处理器是如何进行格式化转换的呢?

其实它底层也调用了一个对象:layouts,也称为formatters,日志内容转换完毕后通过Handler进行日志的输出,最终到达指定的路径

相关组件说明

Loggers

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

Appenders

也被称为handlers,每个logger都会关联一组handlers,logger会将日志交给关联的handlers处理,handlers在此是一个抽象,其具体的实现决定了日志记录的位置可以控制台、文件、网络上的其他日志服务或操作系统日志等

Layouts

也被称为formatters,它负责对日志事件中的数据进行转换和格式化。layouts决定了数据在一条日志记录中的最终形式。

Level

每条日志消息都有一个关联的日志级别。该级别粗略指导了日志消息的重要性和紧迫,可以将level和loggers,appenders做关联以便于我们过滤消息

Filters

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

创建使用Logger对象

要使用J2SE的日志功能,首先要取得java.util.logging.Logger实例,这可以通过Logger类的两个静态getLogger()方法来取得:

// 查找或创建一个logger
static Logger getLogger(String name) 

// 为指定子系统查找或创建一个logger
static Logger getLogger(String name, String resourceBundleName) 
         

!! 注意:name是Logger的名称,当名称相同时候,同一个名称的Logger只创建一个,名称一般建议使用当前类的权限定名称

  代码示例

public class LogDemo {

    @Test
    public void test01(){
        // 1.获取日志记录器对象
        Logger logger = Logger.getLogger("com.test.LogDemo");//当前类权限定名称

        // 2.普通日志记录输出
        logger.info("hello log");

        // 又或者如下打印,参数一是日志级别,同上述logger.info() ,参数二是内容
        logger.log(Level.INFO,"hello log2");

        // 3.通过占位符,输出内容
        String name="wpf";
        int age=18;
        /** @param1 : 日志级别
         *  @param2 : 日志具体内容  {}是占位符,括号里面是数组下标
         *  @param3 : 参数数组,是{}占位符里的具体内容
         */
        logger.log(Level.INFO,"hi {0},{1}",new Object[]{name,age});
    }
}

执行结果

日志级别

在进行信息的记录时,依信息程序的不同,会设定不同等级的信息输出。Java log比log4j的级别详细,全部定义在 java.util.logging.Level 里面,除去off 和 all 两个特殊级别,可分为7种

日志级别(降序)对应的整数使用
OFF最大整数( Integer. MAX_VALUE)

关闭所有级别的日志记录(不捕获任何内容)

SEVERE1000严重故障
WARNING900

警告消息,潜在问题

INFO (默认)800常规运行时信息
CONFIG700配置信息
FINE500

普通的开发人员信息(跟踪消息)

FINER400

详细的开发人员信息(跟踪消息)

FINEST300

高度详细的开发人员信息(跟踪消息)

ALL最小整数(Integer. MIN_VALUE)

打开所有级别的日志记录(捕获所有内容)

结论:级别依次从高到低,其中OFF可用来关闭日志记录,ALL启用所有消息的日志记录

默认日志级别

可以翻阅源码,获取Logger实例时,初始化logger对象赋值的默认日志级别为Info


拓展:自定义日志级别

每个日志级别都有一个整数值,用来确定它们的严重性,除两个特殊的日志级别OFF和ALL之外。如果将日志级别设置在某一个级别上,低于该级别的都会被忽略,不会打印

你也可以定义自己的日志级别,通过继承Level的方式,譬如:

// 自定义日志级别
public class AlertLevel extends Level {

    protected AlertLevel(String name, int value) {
        super(name, value);
    }

    public static void main(String[] args){
        Logger logger = Logger.getLogger("AlertLevel");
        //低于INFO(800),显示不出来,因为默认日志级别是:INFO
        logger.log(new AlertLevel("我是ALERT",950), "自定义 lever!");

        logger.severe("severe 严重信息");      // 1000
        logger.warning("warning 警示信息");    // 900
        logger.info("info 一般信息");          // 800 -------
        logger.config("config 配置方面的信息"); // 700
        logger.fine("fine 细微的信息");        // 500
        logger.finer("finer 更细微的信息");    // 400
        logger.finest("finest 最细微的信息");  // 300
    }
}

执行结果:

结论:由执行结果得知,logger默认的级别是INFO,比INFO更低的级别的日志将不显示。 

设置日志级别

@Test
public void test04() throws IOException {
        // 1 获取日志记录器对象
        Logger logger = Logger.getLogger("com.test.LogDemo");

        // 2 关闭系统默认配置的Handler处理器
        logger.setUseParentHandlers(false);

        // ----------设置日志级别---------------
        // 3 创建Handler处理器类型为:ConsoleHandler控制台输出
        ConsoleHandler consoleHandler = new ConsoleHandler();
        
        // 4 创建Formatter格式化转换组件:将日志内容格式化为标准日志格式
        SimpleFormatter formatter = new SimpleFormatter();

        // 5 进行关联1:设置Handler处理器的格式化组件为SimpleFormatter
        consoleHandler.setFormatter(formatter);

        // 5.1 进行关联2:设置Logger的Handler处理器为ConsoleHandler
        logger.addHandler(consoleHandler);

        // 6.配置日志具体级别:logger日志记录器和Handler处理器都需要设置!! 
        logger.setLevel(Level.ALL);
        consoleHandler.setLevel(Level.ALL);

        // 3.1 创建Handler处理器类型为FileHandler文件输出
        // 一个Logger可以拥有多个handler,每个handler可以有自己的日志级别
        FileHandler fileHandler = new FileHandler("/Users/wpf0113/logs/jul.log");
        fileHandler.setFormatter(formatter);
        logger.addHandler(fileHandler);
        

        // 打印日志信息
        logger.severe("severe 严重信息");

        logger.warning("warning 警示信息");

        logger.info("info 一般信息");

        logger.config("config 配置方面的信息");

        logger.fine("fine 细微的信息");

        logger.finer("finer 更细微的信息");

        logger.finest("finest 最细微的信息");
}

Logger的相关组件说明,以及工作原理:

  1. Logger主要作用是设置日志级别和日志具体内容
  2. handlers处理器:logger会将日志交给关联的handlers处理,handlers决定了日志记录的位置可以是控制台、文件、网络上的其他日志服务或操作系统日志等
  3. formatters格式化组件:handlers底层调用了该组件,主要负责对日志数据进行转换和格式化

所以自定义日志级别,需要设置对应的handlers处理器,也就是代码中的第3步和3.1步骤,其中Formatter 对象可以不用设置,因为我们并没有设置格式化的具体方式,如果用到了再加!

执行结果:

代码中设置的日志级别是ALL,是最低级别,相当于打开所有级别的日志记录

我们设置了两个handler处理器,第一个是控制台输出,如上图结果,全部打印出来了,第二个是文件输出,找到该目录,可以查看日志记录形式

!! 注意:代码中设置的目录 /Users/wpf0113/logs 必须存在否则报错,jul.log是自动创建的

fileHandler默认的输出格式是XML格式。输出格式由java.util.logging.Formatter来控制

Handler处理器类型

输出端类型作用
StreamHandler

写入OutputStream

ConsoleHandler以System.err 方式将日志输出到控制台
FileHandler将信息输出到文件
SocketHandler

写入到远程TCP端口

MemoryHandler写入内存

一个记录器可以有多个处理程序。要获取所有处理程序,我们使用以下代码:

Handler[] handlers = logger.getHandlers();

自定义Handler

通过继承Handler,可定制输出媒介控制器,通常只需实现Handler中三个未定义的抽象方法

  •  publish:主要方法,把日志记录写入你需要的媒介。
  •  flush:清除缓冲区并保存数据。
  •  close:关闭控制器。

通过重写以上三个方法可以很容易实现一个新的输出媒介控制器。

格式化程序(Formatter)

Java SE具有两个内置的Formatter

格式化器类型作用
SimpleFormatter

将LogRecord格式化为字符串

XMLFormatter

将LogRecord格式化为XML格式

 ▸ 我们可以使用以下代码来格式化处理程序

// 格式化成字符串形式
handler.setFormatter(new SimpleFormatter());

// 格式化成XML格式
handler.setFormatter(new XMLFormatter());

SimpleFormatter

ConsolerHandler的默认格式。标准日志格式,输出的日志文件内容就是简单的文字信息

就是我们通常在启动一些诸如Tomcat、JBoss之类的服务器的时候经常能在控制台下看到的那种形式,就像这样:

2004-12-20 23:08:52 org.apache.coyote.http11.Http11Protocol init
信息: Initializing Coyote HTTP/1.1 on http-8080

XMLFormatter

FileHandler的默认格式。以XML形式输出的日志格式,

 如果为Logger添加了一个new XMLFormatter(),那么就会以XML形式输出,例如:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
  <date>2022-03-22T17:32:24</date>
  <millis>1647941544848</millis>
  <sequence>0</sequence>
  <logger>com.test.LogDemo</logger>
  <level>SEVERE</level>
  <class>com.test.LogDemo</class>
  <method>test04</method>
  <thread>1</thread>
  <message>severe 严重信息</message>
</record>

自定义Handler

除了XMLFormatter 与 SimpleFormatter之外,也可以自定义日志的输出格式

继承抽象类Formatter,并重新定义其format()方法即可。format()方法会传入一个LogRecord对象作为参数,可以使用它来取得一些与程序执行有关的信息。

Logger对象的父子关系

Logger的名称决定父子关系

在使用Logger的静态getLogger()方法取得Logger实例时,给getLogger()方法的名称是有意义的。

// logger的父子关系,Logger里面的命名只要包含父级的名称,就自动属于父子关系
@Test
public void test05() throws IOException {
        // 1.获取日志记录器对象
        Logger logger1 = Logger.getLogger("com");

        Logger logger2 = Logger.getLogger("test");

        Logger logger3 = Logger.getLogger("com.test.LogDemo");

        System.out.println(logger3.getParent() == logger1); // true
        System.out.println(logger3.getParent() == logger2); // false
        System.out.println("logger1 parent:"+logger1.getParent());
        System.out.println("logger2 parent:"+logger2.getParent());
        System.out.println("logger3 parent:"+logger3.getParent());

}

执行结果

由上述结果可知

  • 例如 logger3命名 com.test.LogDemo ,则它将继承 logger1 ,该实例的命名包含了logger1,因此默认称为父子关系
  • 例如 logger1命名 com ,则它的父类是RootManager,即根Root,其name是"" ,该root是所有未设置父类的Logger实例的默认父亲类(logger2同理)

或者你也可以理解为包结构,当前LogDemo类的顶级父包为com,上述的 logger1 和 logger3的命名也是如此,存在顶级包含关系,默认为父子,如下:

继承特性

子类会默认从父类身上继承一些特性,例如 level日志级别、handler输出媒介控制器等

@Test
public void test06() throws IOException {
        // 1.获取日志记录器对象
        Logger logger1 = Logger.getLogger("com");
        Logger logger2 = Logger.getLogger("com.test.LogDemo");

        // 2.设置logger1 不继承父类的Handler
        logger1.setUseParentHandlers(false);

        // 3 设置logger1自己的Handler处理器:定义日志输出格式为普通字符串
        ConsoleHandler consoleHandler = new ConsoleHandler();
        logger1.addHandler(consoleHandler);


        // 4.配置logger1的具体日志级别:logger日志记录器和Handler处理器都需要设置!!
        logger1.setLevel(Level.WARNING);
        consoleHandler.setLevel(Level.WARNING);

        // 5.打印logger2的日志:观察logger2继承状态(level日志级别、Handler输出媒介)
        logger2.severe("severe 严重信息");
        logger2.warning("warning 警示信息");
        logger2.info("info 一般信息");
        logger2.config("config 配置方面的信息");
        logger2.fine("fine 细微的信息");
        logger2.finer("finer 更细微的信息");
        logger2.finest("finest 最细微的信息");
}

执行结果:

结论:默认日志级别是Info ,而logger2继承了logger1,我们设置了父类logger1的日志级别为warning,因此最终logger2的打印,只要低于warning的日志将不会打印出来。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值