java日志框架之JUL(Logging)

文章目录

一、JUL简介

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

1、JUL组件介绍

  • Logger:被称为记录器
    • 应用程序通过获取Logger对象,调用其API来发布日志信息
    • Logger通常被认为是访问日志系统的入口程序
  • Handler:处理器
    • 每个Logger都会关联一个或者是一组Handler
    • Logger会将日志交给关联的Handler去做处理,由Handler负责将日志做记录
    • Handler具体实现了日志的输出位置,比如可以输出到控制台或者是文件中等等
  • Filter:过滤器
    • 根据需要定制哪些信息会被记录,哪些信息会被略过
  • Formatter:格式化组件
    • 它负责对日志中的数据和信息进行转换和格式化,所以它决定了我们输出日志最终的形式
  • Level:日志的输出级别
    • 每条日志消息都有一个关联的级别
    • 我们根据输出级别的设置,用来展现最终所呈现的日志信息
    • 根据不同的需求,去设置不同的级别

总结

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

二、Logger快速入门

java 复制代码
import java.util.logging.Level;
import java.util.logging.Logger;

public class JULTest {
    @Test
    public void test01() {
        // Logger对象的创建方式,不能直接new对象
        // 取得对象的方法参数,需要引入当前类的全路径字符串
        Logger logger = Logger.getLogger("com.xc.log.JULTest");

        // 第一种方式: 直接调用日志级别相关的方法,方法中传递日志输出信息
        logger.info("输出info信息1");

        // 第二种方式:调用通用的log方法,然后在里面通过Level类型来定义日志的级别参数,
        // 以及搭配日志输出信息的参数
        logger.log(Level.INFO, "输出info信息2");

        // 日志添加参数
        String name = "zs";
        int age = 23;
        logger.log(Level.INFO, "学生的姓名:{0},年龄: {1}", new Object[]{name, age});
    }
}

输出结果:

java 复制代码
九月 17, 2024 5:45:30 下午 com.xc.log.JULTest test01
信息: 输出info信息1
九月 17, 2024 5:45:30 下午 com.xc.log.JULTest test01
信息: 输出info信息2
九月 17, 2024 5:45:30 下午 com.xc.log.JULTest test01
信息: 学生的姓名:zs,年龄: 23

三、Logger日志级别

1、日志级别

  • java.util.logging.Level中定义了日志的级别
    • SEVERE(最高值)
    • WARNING
    • INFO (默认级别)
    • CONFIG
    • FINE
    • FINER
    • FINEST(最低值)
  • 还有两个特殊的级别
    • OFF,可用来关闭日志记录
    • ALL,启用所有消息的日志记录
java 复制代码
public class Level implements java.io.Serializable {

	...
	
	//错误 --- 最高级的日志级别
	public static final Level SEVERE = new Level("SEVERE",1000, defaultBundle);
	//WARNING : 警告
	public static final Level WARNING = new Level("WARNING", 900, defaultBundle);
	//INFO : (默认级别)消息
	public static final Level INFO = new Level("INFO", 800, defaultBundle);
	//CONFIG : 配置
	public static final Level CONFIG = new Level("CONFIG", 700, defaultBundle);
	//FINE : 详细信息
	public static final Level FINE = new Level("FINE", 500, defaultBundle);
	//FINER : 较详细信息
	public static final Level FINER = new Level("FINER", 400, defaultBundle);
	//FINEST : 非常详细信息 --- 最低级的日志级别
	public static final Level FINEST = new Level("FINEST", 300, defaultBundle);
	
	
	//OFF 可用来关闭日志记录
	public static final Level OFF = new Level("OFF",Integer.MAX_VALUE, defaultBundle);
	//ALL 启用所有消息的日志记录
	public static final Level ALL = new Level("ALL", Integer.MIN_VALUE, defaultBundle);
	...
}

2、默认级别info

java 复制代码
@Test
public void test02(){
    Logger logger = Logger.getLogger("com.xc.log.JULTest");
    //打印日志
    logger.severe("severe信息");
    logger.warning("warning信息");
    logger.info("info信息");
    logger.config("config信息");
    logger.fine("fine信息");
    logger.finer("finer信息");
    logger.finest("finest信息");
}

输出结果:

java 复制代码
九月 17, 2024 6:36:00 下午 com.xc.log.JULTest test02
严重: severe信息
九月 17, 2024 6:36:00 下午 com.xc.log.JULTest test02
警告: warning信息
九月 17, 2024 6:36:00 下午 com.xc.log.JULTest test02
信息: info信息

3、原理分析

  • 以SEVERE、WARNING、INFO级别为例
java 复制代码
public class Logger {
	...

	// 严重错误
    public void severe(String msg) {
        log(Level.SEVERE, msg);
    }
	
	// 警告信息
    public void warning(String msg) {
        log(Level.WARNING, msg);
    }
	
	// 记录信息
    public void info(String msg) {
        log(Level.INFO, msg);
    }
	
	// 无论哪种类型都会进入此方法,只是传入不同的级别参数
    public void log(Level level, String msg) {
    	// 判断日志级别是否需要打印
        if (!isLoggable(level)) {
            return;
        }
        LogRecord lr = new LogRecord(level, msg);
        doLog(lr);
    }
	
	// levelValue为默认级别Level.INFO的数值800
	// offValue为关闭日志级别Level.OFF的数值Integer.MAX_VALUE
    public boolean isLoggable(Level level) {
        if (level.intValue() < levelValue || levelValue == offValue) {
            return false;
        }
        return true;
    }
}
  • levelValue为默认级别Level.INFO的数值800
  • 大于等于800SEVERE-1000WARNING-900INFO-800三个级别,isLoggable方法返回true,都可以输出
  • 当默认级别为Level.OFF的数值Integer.MAX_VALUE,isLoggable方法返回false,都不可以输出
  • 当默认级别为Level.ALL的数值Integer.MIN_VALUE,isLoggable方法返回true,都可以输出

4、自定义日志级别

java 复制代码
@Test
public void test03() {
    // 日志记录器
    Logger logger = Logger.getLogger("com.xc.log.JULTest");

    // 关闭系统默认配置
    logger.setUseParentHandlers(false);

    // 处理器Handler
    // 在此我们使用的是控制台日志处理器,取得处理器对象
    ConsoleHandler handler = new ConsoleHandler();
    // 在处理器中设置输出格式
    handler.setFormatter(new SimpleFormatter());

    // 在记录器中添加处理器
    logger.addHandler(handler);

    // 设置日志的打印级别
    // 此处必须将日志记录器和处理器的级别进行统一的设置,才会达到日志显示相应级别的效果
    logger.setLevel(Level.ALL); // 日志记录器
    handler.setLevel(Level.ALL); // 日志处理器

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

输出结果:

java 复制代码
九月 17, 2024 7:18:11 下午 com.xc.log.JULTest test03
严重: severe信息
九月 17, 2024 7:18:11 下午 com.xc.log.JULTest test03
警告: warning信息
九月 17, 2024 7:18:11 下午 com.xc.log.JULTest test03
信息: info信息
九月 17, 2024 7:18:11 下午 com.xc.log.JULTest test03
配置: config信息
九月 17, 2024 7:18:11 下午 com.xc.log.JULTest test03
详细: fine信息
九月 17, 2024 7:18:11 下午 com.xc.log.JULTest test03
较详细: finer信息
九月 17, 2024 7:18:11 下午 com.xc.log.JULTest test03
非常详细: finest信息

5、日志持久化(保存到磁盘)

  • 用户使用Logger来进行日志的记录,Logger可以持有多个处理器Handler
  • 日志的记录使用的是Logger,日志的输出使用的是Handler
  • 可以输出到控制台也可以输出到文件,也可以两者兼备
java 复制代码
@Test
public void test04() throws IOException {
    Logger logger = Logger.getLogger("com.xc.log.JULTest");
    logger.setUseParentHandlers(false);

    // 文件日志处理器
    FileHandler handler = new FileHandler("/Users/xuchang/Documents/javaCode/data/logs/jul.log");
    SimpleFormatter formatter = new SimpleFormatter();
    handler.setFormatter(formatter);
    logger.addHandler(handler);

    // 也可以同时在控制台和文件中进行打印
    ConsoleHandler handler2 = new ConsoleHandler();
    handler2.setFormatter(formatter);
    // 可以在记录器中同时添加多个处理器
    logger.addHandler(handler2);

    // 设置日志的打印级别
    logger.setLevel(Level.ALL);
    // 输出到文件的级别为all
    handler.setLevel(Level.INFO);
    // 输出到控制台的解绑为config
    handler2.setLevel(Level.CONFIG);

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

控制台config日志级别输出如下:

文件info日志级别输出如下:

三、Logger父子关系

  • JUL中Logger之间存在父子关系,这种父子关系通过树状结构存储
  • JUL在初始化时会创建一个顶层RootLogger作为所有Logger父Logger,存储上作为树状结构的根节点
  • 父子关系通过路径来关联
java 复制代码
@Test
public void test05() {
    // 父亲是RootLogger,名称默认是一个空的字符串
    // RootLogger可以被称之为所有logger对象的顶层logger
    Logger logger1 = Logger.getLogger("com.xc");
    Logger logger2 = Logger.getLogger("com.xc.log");
    Logger logger3 = Logger.getLogger("com.xc.log.JULTest");

    System.out.println("logger1的父Logger引用为:"
            + logger1.getParent() + "; 名称为" + logger1.getName() + ";" +
            " 父亲的名称为" + logger1.getParent().getName());

    System.out.println("logger2的父Logger引用为:"
            + logger2.getParent() + "; 名称为" + logger2.getName() + ";" +
            " 父亲的名称为" + logger2.getParent().getName());

    System.out.println("logger3的父Logger引用为:"
            + logger3.getParent() + "; 名称为" + logger3.getName() + ";" +
            " 父亲的名称为" + logger3.getParent().getName());
}

输出结果:

父亲所做的设置,也能够同时作用于儿子

logger1是logger2的父级,父级logger1设置日志级别为ALL,即使子级什么也不设置(默认info级别),最后通过打印可看到,子级跟随父级已经设置为ALL

java 复制代码
@Test
public void test06() {
    Logger logger1 = Logger.getLogger("com.xc.log");
    Logger logger2 = Logger.getLogger("com.xc.log.JULTest");
    // 父亲设置级别为all
    logger1.setUseParentHandlers(false);
    ConsoleHandler handler = new ConsoleHandler();
    SimpleFormatter formatter = new SimpleFormatter();
    handler.setFormatter(formatter);
    logger1.addHandler(handler);
    handler.setLevel(Level.ALL);
    logger1.setLevel(Level.ALL);
    
    // 儿子默认级别为info
    logger2.severe("severe信息");
    logger2.warning("warning信息");
    logger2.info("info信息");
    logger2.config("config信息");
    logger2.fine("fine信息");
    logger2.finer("finer信息");
    logger2.finest("finest信息");
}

输出结果:

java 复制代码
九月 17, 2024 9:05:57 下午 com.xc.log.JULTest test06
严重: severe信息
九月 17, 2024 9:05:57 下午 com.xc.log.JULTest test06
警告: warning信息
九月 17, 2024 9:05:57 下午 com.xc.log.JULTest test06
信息: info信息
九月 17, 2024 9:05:57 下午 com.xc.log.JULTest test06
配置: config信息
九月 17, 2024 9:05:57 下午 com.xc.log.JULTest test06
详细: fine信息
九月 17, 2024 9:05:57 下午 com.xc.log.JULTest test06
较详细: finer信息
九月 17, 2024 9:05:57 下午 com.xc.log.JULTest test06
非常详细: finest信息

四、Logger配置文件

  • 默认读取配置文件路径$JAVAHOME\jre\lib\logging.properties
yaml 复制代码
# 全局性质
############################################################
# RootLogger使用的处理器
handlers=java.util.logging.ConsoleHandler
# RootLogger日志等级
.level = INFO
############################################################

# 处理器指定属性,描述处理程序的特定配置信息
############################################################
# 文件处理器属性设置
# 默认输出的日志文件路径,位于用户的主目录中
java.util.logging.FileHandler.pattern = %h/java%u.log
# 默认输出的日志文件大小(单位字节)
java.util.logging.FileHandler.limit = 50000
# 默认输出的日志文件数量
java.util.logging.FileHandler.count = 1
# 默认输出的日志文件格式(XML)
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
 
# 控制台处理器属性设置
# 默认输出的日志级别
java.util.logging.ConsoleHandler.level = INFO
# 默认输出的日志格式(Simple)
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
############################################################

# 配置特定属性,为每个记录器提供额外的控制
############################################################
# 例如:将日志级别设定到具体的某个包下
com.xyz.foo.level = SEVERE
############################################################
  • 自定义配置文件,将配置文件添加到resources目录下
java 复制代码
@Test
public void test07() throws Exception {
    // 读取自定义配置文件
    InputStream in = JULTest.class.getClassLoader().getResourceAsStream("logging.properties");
    // 获取日志管理器对象
    LogManager logManager = LogManager.getLogManager();
    // 通过日志管理器加载配置文件
    logManager.readConfiguration(in);
    Logger logger = Logger.getLogger("com.xc.log.JULTest");
    logger.severe("severe");
    logger.warning("warning");
    logger.info("info");
    logger.config("config");
    logger.fine("fine");
    logger.finer("finer");
    logger.finest("finest");
}
  • 可以忽略父级配置,添加自定义配置,设置默认日志级别、日志追加(默认覆盖)等
yml 复制代码
## RootLogger使用的处理器(获取时设置)
handlers=java.util.logging.ConsoleHandler
# RootLogger日志等级
.level=INFO

## 自定义Logger
com.xc.handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler
# 自定义Logger日志等级
com.xc.level=CONFIG
# 忽略父日志设置
com.xc.useParentHandlers=false

## 控制台处理器
# 输出日志级别
java.util.logging.ConsoleHandler.level=CONFIG
# 输出日志格式
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter

## 文件处理器
# 输出日志级别
java.util.logging.FileHandler.level=CONFIG
# 输出日志格式
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
# 输出日志文件路径
java.util.logging.FileHandler.pattern=/Users/xuchang/Documents/javaCode/data/logs/java%u.log
# 输出日志文件限制大小(50000字节)
java.util.logging.FileHandler.limit=50000
# 输出日志文件限制个数
java.util.logging.FileHandler.count=10
# 输出日志文件 是否是追加
java.util.logging.FileHandler.append=true

控制台日志

文件追加日志

相关推荐
mghio39 分钟前
Dubbo 中的集群容错
java·微服务·dubbo
范文杰2 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪3 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪3 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy3 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom4 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom4 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom4 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom4 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试