Java 日志框架 JUL 详解大全

2023-10-16 23:40

本文主要是介绍Java 日志框架 JUL 详解大全,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • JUL 简介
    • JUL 架构
    • 入门示例
    • Logger 父子继承关系
    • 日志配置
      • 硬编码日志配置
      • 日志配置文件
      • 自定义 Logger
      • 自定义日志格式
      • 日志过滤器

JUL 简介

JUL(Java util logging),Java 原生日志框架,不需要引入第三方依赖包,使用简单方便,一般在小型应用中使用,主流项目中现在很少使用了。

JUL 架构

在这里插入图片描述

  • Application:Java 应用程序。
  • Logger:记录器,Java 应用程序通过调用记录器的方法来发布日志记录。
  • Handler:处理器,每一个 Logger 都可以关联一个或多个 Handler,Handler 从 Logger 获取日志并将日志输出到某个目的地,目的地可以是控制台,本地文件,或网络日志服务,或将它们转发到操作系统日志等等。通过Handler.setLevel(level.off)方法可以禁用一个 Handler,也可以设置其他级别来开启此 Handler。
  • Filter:过滤器,根据条件过滤哪些日志记录。每一个 Logger 和 Handler 都可以关联一个 Filter。
  • Formatter :格式化器,负责对日志事件中的日志记录进行转换和格式化。
  • Level:每一条日志记录都有一个关联的日志级别,表示此条日志的重要性和紧急程度。也可以对 Logger 和 Handler 设置关联的日志级别。

入门示例

package com.chenpi;import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.Test;/*** @author 陈皮* @version 1.0* @description* @date 2022/3/2*/
public class ChenPiJULMain {// JUL 日志框架演示@Testpublic void testLog() {// 获取日志记录器对象Logger logger = Logger.getLogger("com.chenpi.ChenPiJULMain");// 日志记录输出logger.severe(">>>>> Hello ChenPi!!");logger.warning(">>>>> Hello ChenPi!!");logger.info(">>>>> Hello ChenPi!!"); // 默认日志级别logger.config(">>>>> Hello ChenPi!!");logger.fine(">>>>> Hello ChenPi!!");logger.finer(">>>>> Hello ChenPi!!");logger.finest(">>>>> Hello ChenPi!!");// 输出指定日志级别的日志记录logger.log(Level.WARNING, ">>>>> Hello Warning ChenPi!!");// 占位符形式String name = "ChenPi";int age = 18;logger.log(Level.INFO, ">>>>> Hello {0},{1} years old!", new Object[]{name, age});// 异常堆栈信息logger.log(Level.SEVERE, ">>>>> Hello NPE!", new NullPointerException());}
}

JUL 默认将日志信息输出到控制台,默认日志级别是 info。控制台输出结果如下:

在这里插入图片描述

Logger 父子继承关系

在 JUL 中,Logger 有父子继承关系概念,会根据 Logger 对象的名称的包含关系,划分父子继承关系。对于某个 Logger 对象,如果找不到显性创建的父 Logger 对象,那么它的父 Logger 是根 Logger,即 RootLogger。

子代 Logger 对象会继承父 Logger 的配置,例如日志级别,关联的 Handler,日志格式等等。对于任何 Logger 对象,如果没对其做特殊配置,那么它最终都会继承 RootLogger 的配置。

package com.chenpi;import java.util.logging.Logger;
import org.junit.Test;/*** @author 陈皮* @version 1.0* @description* @date 2022/3/2*/
public class ChenPiJULMain {@Testpublic void testLog() {// logger1的父Logger是RootLoggerLogger logger1 = Logger.getLogger("com.chenpi.a");// logger2的父Logger是logger1  Logger logger2 = Logger.getLogger("com.chenpi.a.b.c");Logger logger1Parent = logger1.getParent();System.out.println("logger1Parent:" + logger1Parent + ",name:" + logger1Parent.getName());Logger logger2Parent = logger2.getParent();System.out.println("logger1:" + logger1 + ",name:" + logger1.getName());System.out.println("logger2Parent:" + logger2Parent + ",name:" + logger2Parent.getName());}
}// 输出结果如下
logger1Parent:java.util.logging.LogManager$RootLogger@61e717c2,name:
logger1:java.util.logging.Logger@66cd51c3,name:com.chenpi.a
logger2Parent:java.util.logging.Logger@66cd51c3,name:com.chenpi.a

日志配置

我们可以通过2种方式调整 JUL 默认的日志行为(设置日志级别,日志输出目的地,日志格式等等),一种是通过在程序中硬编码形式(不推荐),另一种是通过单独的配置文件形式。

硬编码日志配置

在 JUL 中,Logger 是有父子继承关系的,所以当我们需要对某一个 Logger 对象进行单独的配置时,需要将它设置为不继承使用父 Logger 的配置。

以下演示名称为com.chenpi.ChenPiJULMain的 Logger 对象单独进行配置,并且关联两个 Handler。

package com.chenpi;import java.io.IOException;
import java.util.logging.*;
import org.junit.Test;/*** @author 陈皮* @version 1.0* @description* @date 2022/3/2*/
public class ChenPiJULMain {@Testpublic void testLog() throws IOException {// 获取日志记录器对象Logger logger = Logger.getLogger("com.chenpi.ChenPiJULMain");// 关闭默认配置,即不使用父Logger的Handlerslogger.setUseParentHandlers(false);// 设置记录器的日志级别为ALLlogger.setLevel(Level.ALL);// 日志记录格式,使用简单格式转换对象SimpleFormatter simpleFormatter = new SimpleFormatter();// 控制台输出Handler,并且设置日志级别为INFO,日志记录格式ConsoleHandler consoleHandler = new ConsoleHandler();consoleHandler.setLevel(Level.INFO);consoleHandler.setFormatter(simpleFormatter);// 文件输出Handler,并且设置日志级别为FINE,日志记录格式FileHandler fileHandler = new FileHandler("./jul.log");fileHandler.setLevel(Level.FINE);fileHandler.setFormatter(simpleFormatter);// 记录器关联处理器,即此logger对象的日志信息输出到这两个Handler进行处理logger.addHandler(consoleHandler);logger.addHandler(fileHandler);// 日志记录输出logger.severe(">>>>> Hello ChenPi!!");logger.warning(">>>>> Hello ChenPi!!");logger.info(">>>>> Hello ChenPi!!");logger.config(">>>>> Hello ChenPi!!");logger.fine(">>>>> Hello ChenPi!!");logger.finer(">>>>> Hello ChenPi!!");logger.finest(">>>>> Hello ChenPi!!");}
}

因为 Logger 设置的日志级别是 ALL,即所有级别的日志记录都可以通过。但是 ConsoleHandler 设置的日志级别是 INFO,所以控制台只输出 INFO 级别以上的日志记录。而 FileHandler 设置的日志级别是 FINE,所以日志文件中输出的是 FINE 级别以上的日志记录。

以下是控制台输出的日志结果:

在这里插入图片描述

以下是日志文件jul.log中输出的日志结果:

在这里插入图片描述

日志配置文件

通过 debug 调试,按以下方法顺序,可以发现,如果我们没有配置 JUL 的配置文件,系统默认从 JDK 的安装目录下的 lib 目录下读取默认的配置文件logging.properties

getLogger()  -> demandLogger() -> LogManager.getLogManager() -> ensureLogManagerInitialized() -> owner.readPrimordialConfiguration() -> readConfiguration()

在这里插入图片描述

在这里插入图片描述

以下是 JDK 自带的 JUL 默认日志配置文件内容:

在这里插入图片描述

对于配置选项有哪些,其实可以通过相应类的源码找出,例如 Logger 类的源码如下:

在这里插入图片描述

FileHandler 类的源码如下:

在这里插入图片描述

所以我们可以拷贝默认的配置文件到我们工程的 resources 目录下,自定义修改配置信息。

############################################################
# 全局属性
############################################################# 顶级RootLogger关联的Handler,多个Handler使用逗号隔开
# 对于其他Logger,如果没有指定自己的Handler,则默认继承此
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler# 默认全局日志级别,Logger和Handler都可以设置自己的日志级别来覆盖此级别
.level= ALL############################################################
# Handler 配置
############################################################# FileHandler定义
# 日志文件存储位置
java.util.logging.FileHandler.pattern = ./jul%u.log
# 单个文件的最大字节数,0代表不限制
java.util.logging.FileHandler.limit = 50000
# 文件数量上限,多个文件为jul0.log.0,jul0.log.1 ...
java.util.logging.FileHandler.count = 5
# 日志级别
java.util.logging.FileHandler.level = SEVERE
# 日志追加方式
java.util.logging.FileHandler.append = true
# Handler对象采用的字符集
java.util.logging.FileHandler.encoding = UTF-8
# 日志格式,使用系统默认的简单格式
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter# ConsoleHandler定义
# 日志级别
java.util.logging.ConsoleHandler.level = INFO
# Handler对象采用的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
# 日志格式,使用系统默认的简单格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter############################################################
# Logger 配置
############################################################# 设置名称为com.chenpi.person的Logger对象的日志级别为WARNING
com.chenpi.person.level = WARNING

然后我们应用中加载我们类路径中自定义的配置文件。

package com.chenpi;import java.io.IOException;
import java.io.InputStream;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import org.junit.Test;/*** @author 陈皮* @version 1.0* @description* @date 2022/3/2*/
public class ChenPiJULMain {// JUL 日志框架演示@Testpublic void testLog() throws IOException {// 读取配置文件// 也可以通过使用java.util.logging.config.file系统属性指定文件名// 例如 java -Djava.util.logging.config.file=myfileInputStream resourceAsStream = ChenPiJULMain.class.getClassLoader().getResourceAsStream("logging.properties");// 获取LogManagerLogManager logManager = LogManager.getLogManager();// 记载配置文件logManager.readConfiguration(resourceAsStream);// 获取日志记录器对象Logger logger = Logger.getLogger("com.chenpi.ChenPiJULMain");// 日志记录输出logger.severe(">>>>> Hello ChenPi!!");logger.warning(">>>>> Hello ChenPi!!");logger.info(">>>>> Hello ChenPi!!");logger.config(">>>>> Hello ChenPi!!");logger.fine(">>>>> Hello ChenPi!!");logger.finer(">>>>> Hello ChenPi!!");logger.finest(">>>>> Hello ChenPi!!");// 获取日志记录器对象Logger personLogger = Logger.getLogger("com.chenpi.person");// 日志记录输出personLogger.severe(">>>>> Hello Person!!");personLogger.warning(">>>>> Hello Person!!");personLogger.info(">>>>> Hello Person!!");personLogger.config(">>>>> Hello Person!!");personLogger.fine(">>>>> Hello Person!!");personLogger.finer(">>>>> Hello Person!!");personLogger.finest(">>>>> Hello Person!!");}
}

控制台和文件中输出内容如下:

在这里插入图片描述

在这里插入图片描述

自定义 Logger

我们可以针对某一个 Logger 进行单独的配置,例如日志级别,关联的 Handler 等,而不默认继承父级的。

当然我们也可以为以包名为名称的 Logger 进行配置,这样这个包名下的所有子代 Logger 都能继承此配置。

############################################################
# 全局属性
############################################################# 顶级RootLogger关联的Handler,多个Handler使用逗号隔开
# 对于其他Logger,如果没有指定自己的Handler,则默认继承此
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler# 默认全局日志级别,Logger和Handler都可以设置自己的日志级别来覆盖此级别
.level= ALL############################################################
# Handler 配置
############################################################# FileHandler定义
# 日志文件存储位置
java.util.logging.FileHandler.pattern = ./jul%u.log
# 单个文件的最大字节数,0代表不限制
java.util.logging.FileHandler.limit = 50000
# 文件数量上限
java.util.logging.FileHandler.count = 5
# 日志级别
java.util.logging.FileHandler.level = SEVERE
# 日志追加方式
java.util.logging.FileHandler.append = true
# Handler对象采用的字符集
java.util.logging.FileHandler.encoding = UTF-8
# 日志格式,使用系统默认的简单格式
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter# ConsoleHandler定义
# 日志级别
java.util.logging.ConsoleHandler.level = INFO
# Handler对象采用的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
# 日志格式,使用系统默认的简单格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter############################################################
# Logger 配置
############################################################# 设置名称为com.chenpi.person的Logger对象的日志级别为WARNING
com.chenpi.person.level = WARNING
# 只关联FileHandler
com.chenpi.person.handlers = java.util.logging.FileHandler
# 关闭默认配置
com.chenpi.person.useParentHandlers = false
package com.chenpi;import java.io.IOException;
import java.io.InputStream;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import org.junit.Test;/**
* @author 陈皮
* @version 1.0
* @description
* @date 2022/3/2
*/
public class ChenPiJULMain {// JUL 日志框架演示@Testpublic void testLog() throws IOException {// 读取配置文件InputStream resourceAsStream = ChenPiJULMain.class.getClassLoader().getResourceAsStream("logging.properties");// 获取LogManagerLogManager logManager = LogManager.getLogManager();// 记载配置文件logManager.readConfiguration(resourceAsStream);// 获取日志记录器对象Logger logger = Logger.getLogger("com.chenpi.ChenPiJULMain");// 日志记录输出logger.severe(">>>>> Hello ChenPi!!");logger.warning(">>>>> Hello ChenPi!!");logger.info(">>>>> Hello ChenPi!!");logger.config(">>>>> Hello ChenPi!!");logger.fine(">>>>> Hello ChenPi!!");logger.finer(">>>>> Hello ChenPi!!");logger.finest(">>>>> Hello ChenPi!!");// 获取日志记录器对象Logger personLogger = Logger.getLogger("com.chenpi.person");// 日志记录输出personLogger.severe(">>>>> Hello Person!!");personLogger.warning(">>>>> Hello Person!!");personLogger.info(">>>>> Hello Person!!");personLogger.config(">>>>> Hello Person!!");personLogger.fine(">>>>> Hello Person!!");personLogger.finer(">>>>> Hello Person!!");personLogger.finest(">>>>> Hello Person!!");}
}

上述例子中,对于名称为com.chenpi.person的 Logger,只将日志输出到文件中,其他 Logger 则会同时输出到控制台和文件中。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

自定义日志格式

SimpleFormatter类源码为例,再到LoggingSupport类源码,发现首先判断我们是否通过java.util.logging.SimpleFormatter.format属性配置了格式,如果没有则使用默认的日志格式。

在这里插入图片描述

在这里插入图片描述

所以我们可以在配置文件中自定义日志记录的格式。

############################################################
# 全局属性
############################################################# 顶级RootLogger关联的Handler,多个Handler使用逗号隔开
# 对于其他Logger,如果没有指定自己的Handler,则默认继承此
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler# 默认全局日志级别,Logger和Handler都可以设置自己的日志级别来覆盖此级别
.level= ALL############################################################
# Handler 配置
############################################################# FileHandler定义
# 日志文件存储位置
java.util.logging.FileHandler.pattern = ./jul%u.log
# 单个文件的最大字节数,0代表不限制
java.util.logging.FileHandler.limit = 1024
# 文件数量上限
java.util.logging.FileHandler.count = 5
# 日志级别
java.util.logging.FileHandler.level = FINE
# 日志追加方式
java.util.logging.FileHandler.append = true
# Handler对象采用的字符集
java.util.logging.FileHandler.encoding = UTF-8
# 日志格式,使用系统默认的简单格式
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
# 自定义SimpleFormatter的日志格式
java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n# ConsoleHandler定义
# 日志级别
java.util.logging.ConsoleHandler.level = INFO
# Handler对象采用的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
# 日志格式,使用系统默认的简单格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter############################################################
# Logger 配置
############################################################# 设置名称为com.chenpi.person的Logger对象的日志级别为WARNING
com.chenpi.person.level = WARNING

这样,所有绑定了 SimpleFormatter 的 Handler 的所有日志记录就使用了自定义的格式。

在这里插入图片描述

当然通过源码发现,日志格式类还有其他几个实现,如下所示:

在这里插入图片描述

日志过滤器

Filter,日志过滤器,用来对输出的日志记录进行过滤。我们可以根据多个维度进行过滤,例如只输出 message 包含某段文本信息的日志,只输出某个方法中记录的日志,某个级别的日志等等。

Logger 对象将日志信息包装成一个 LogRecord 对象,然后将该对象传给 Handler 进行处理。每一个 Logger 和 Handler 都可以关联一个 Filter。LogRecord 中包含了日志的文本信息、日志生成的时间戳、日志来自于哪个类、日志来自于哪个方法、日志来自于哪个线程等等信息。

Filter 源码如下所示,我们只需要创建一个 Filter 的实现类,重写方法即可。

package java.util.logging;/**
* A Filter can be used to provide fine grain control over
* what is logged, beyond the control provided by log levels.
* <p>
* Each Logger and each Handler can have a filter associated with it.
* The Logger or Handler will call the isLoggable method to check
* if a given LogRecord should be published.  If isLoggable returns
* false, the LogRecord will be discarded.
*
* @since 1.4
*/
@FunctionalInterface
public interface Filter {/*** Check if a given log record should be published.* @param record  a LogRecord* @return true if the log record should be published.*/public boolean isLoggable(LogRecord record);
}

我们实现一个 Filter ,如果日志消息包含暴力两个字,则不予放行,即不记录此条日志。

package com.chenpi;import java.util.logging.Filter;
import java.util.logging.LogRecord;/*** @author 陈皮* @version 1.0* @description* @date 2022/3/2*/
public class MyLoggerFilter implements Filter {private static final String SENSITIVE_MESSAGE = "暴力";@Overridepublic boolean isLoggable(LogRecord record) {String message = record.getMessage();return null == message || !message.contains(SENSITIVE_MESSAGE);}
}

然后我们将此过滤器对象设置绑定到 Logger 中即可。

package com.chenpi;import java.io.IOException;
import java.io.InputStream;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import org.junit.Test;/*** @author 陈皮* @version 1.0* @description* @date 2022/3/2*/
public class ChenPiJULMain {// JUL 日志框架演示@Testpublic void testLog() throws IOException {// 读取配置文件InputStream resourceAsStream = ChenPiJULMain.class.getClassLoader().getResourceAsStream("logging.properties");// 获取LogManagerLogManager logManager = LogManager.getLogManager();// 记载配置文件logManager.readConfiguration(resourceAsStream);// 获取日志记录器对象Logger logger = Logger.getLogger("com.chenpi.ChenPiJULMain");// Logger关联过滤器logger.setFilter(new MyLoggerFilter());// 日志记录输出logger.info(">>>>> Hello ChenPi!!");logger.info(">>>>> 暴力小孩!!");}
}// 输出结果如下
信息: >>>>> Hello ChenPi!! [星期三 三月 02 15:31:06 CST 2022]

本次分享到此结束啦~~

我是陈皮,一个在互联网 Coding 的 ITer。如果觉得文章对你有帮助,点赞、收藏、关注、评论,您的支持就是我创作最大的动力!

这篇关于Java 日志框架 JUL 详解大全的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/221515

相关文章

SpringBoot实现RSA+AES自动接口解密的实战指南

《SpringBoot实现RSA+AES自动接口解密的实战指南》在当今数据泄露频发的网络环境中,接口安全已成为开发者不可忽视的核心议题,RSA+AES混合加密方案因其安全性高、性能优越而被广泛采用,本... 目录一、项目依赖与环境准备1.1 Maven依赖配置1.2 密钥生成与配置二、加密工具类实现2.1

在Java中实现线程之间的数据共享的几种方式总结

《在Java中实现线程之间的数据共享的几种方式总结》在Java中实现线程间数据共享是并发编程的核心需求,但需要谨慎处理同步问题以避免竞态条件,本文通过代码示例给大家介绍了几种主要实现方式及其最佳实践,... 目录1. 共享变量与同步机制2. 轻量级通信机制3. 线程安全容器4. 线程局部变量(ThreadL

Go语言使用select监听多个channel的示例详解

《Go语言使用select监听多个channel的示例详解》本文将聚焦Go并发中的一个强力工具,select,这篇文章将通过实际案例学习如何优雅地监听多个Channel,实现多任务处理、超时控制和非阻... 目录一、前言:为什么要使用select二、实战目标三、案例代码:监听两个任务结果和超时四、运行示例五

Linux线程同步/互斥过程详解

《Linux线程同步/互斥过程详解》文章讲解多线程并发访问导致竞态条件,需通过互斥锁、原子操作和条件变量实现线程安全与同步,分析死锁条件及避免方法,并介绍RAII封装技术提升资源管理效率... 目录01. 资源共享问题1.1 多线程并发访问1.2 临界区与临界资源1.3 锁的引入02. 多线程案例2.1 为

分布式锁在Spring Boot应用中的实现过程

《分布式锁在SpringBoot应用中的实现过程》文章介绍在SpringBoot中通过自定义Lock注解、LockAspect切面和RedisLockUtils工具类实现分布式锁,确保多实例并发操作... 目录Lock注解LockASPect切面RedisLockUtils工具类总结在现代微服务架构中,分布

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

破茧 JDBC:MyBatis 在 Spring Boot 中的轻量实践指南

《破茧JDBC:MyBatis在SpringBoot中的轻量实践指南》MyBatis是持久层框架,简化JDBC开发,通过接口+XML/注解实现数据访问,动态代理生成实现类,支持增删改查及参数... 目录一、什么是 MyBATis二、 MyBatis 入门2.1、创建项目2.2、配置数据库连接字符串2.3、入

Springboot项目启动失败提示找不到dao类的解决

《Springboot项目启动失败提示找不到dao类的解决》SpringBoot启动失败,因ProductServiceImpl未正确注入ProductDao,原因:Dao未注册为Bean,解决:在启... 目录错误描述原因解决方法总结***************************APPLICA编