当前位置: 首页 > news >正文

Java-01-基础篇-JDK日志(JUL)

一,JUL

        JDK 自带的日志(java.util.logging,简称 JUL)这是 Java 标准库自带的日志框架,无需额外依赖

1.1 基本实现

package com.toast.learn.spring.core.logger.jdk;import java.util.logging.Level;
import java.util.logging.Logger;/*** @author toast* @time 2025/9/11* @remark*/
public class JDKLoggingSimpleExample {private static final Logger logger = Logger.getLogger(JdkLoggingExample.class.getName());public static void main(String[] args) {logger.info("This is an info message");logger.warning("This is a warning message");logger.severe("This is a severe (error) message");// 可设置日志级别logger.setLevel(Level.ALL);}
}

输出结果

> Task :spring-learn:run
9月 11, 2025 6:05:51 下午 com.toast.learn.spring.core.logger.jdk.JDKLoggingExample01 main
信息: This is an info message
9月 11, 2025 6:05:51 下午 com.toast.learn.spring.core.logger.jdk.JDKLoggingExample01 main
警告: This is a warning message
9月 11, 2025 6:05:51 下午 com.toast.learn.spring.core.logger.jdk.JDKLoggingExample01 main
严重: This is a severe (error) message

可以看出,JDK自带的支持国际化,会根据本地的Locale 选择输出对应的日志级别内容

二,日志级别

JDK的日志级别,定义在 java.util.logging.Level 类中,部分代码如下:

package java.util.logging;public class Level implements Serializable {private static final String defaultBundle = "sun.util.logging.resources.logging";private final String name;private final int value;private final String resourceBundleName;private transient String localizedLevelName;private transient Locale cachedLocale;public static final Level OFF 		= new Level("OFF", Integer.MAX_VALUE, "sun.util.logging.resources.logging");public static final Level SEVERE 	= new Level("SEVERE", 1000, "sun.util.logging.resources.logging");public static final Level WARNING 	= new Level("WARNING", 900, "sun.util.logging.resources.logging");public static final Level INFO 		= new Level("INFO", 800, "sun.util.logging.resources.logging");public static final Level CONFIG 	= new Level("CONFIG", 700, "sun.util.logging.resources.logging");public static final Level FINE 		= new Level("FINE", 500, "sun.util.logging.resources.logging");public static final Level FINER 	= new Level("FINER", 400, "sun.util.logging.resources.logging");public static final Level FINEST 	= new Level("FINEST", 300, "sun.util.logging.resources.logging");public static final Level ALL 		= new Level("ALL", Integer.MIN_VALUE, "sun.util.logging.resources.logging");private static final Level[] standardLevels;private static final long serialVersionUID = -8176160795706313070L;/** 忽略其他代码 */
}

        从这里也可以看出日志级别的国际化并没有直接国际化的内容,日志级别的国际化的相关资源都存储在指定的位置。

        日志级别的国际化资源包存储位置:"sun.util.logging.resources.logging"

第三个参数是资源包名(ResourceBundle),用于:

本地化级别名称(如中文环境下显示“信息”而不是“INFO”)

提供标准错误消息模板 messages_zh_CN.properties 。

public final class logging_zh_CN extends ListResourceBundle {public logging_zh_CN() {}protected final Object[][] getContents() {return new Object[][]{{"ALL", "全部"}, {"CONFIG", "配置"}, {"FINE", "详细"}, {"FINER", "较详细"}, {"FINEST", "非常详细"}, {"INFO", "信息"}, {"OFF", "禁用"}, {"SEVERE", "严重"}, {"WARNING", "警告"}};}
}

2.1 完整级别列表(按严重程度从高到低排序)

OFF

Integer.MAX_VALUE

关闭所有日志,最高“级别”,实际用于禁用日志输出

临时关闭日志、性能压测

SEVERE

1000

严重错误,系统不可用、关键功能失败(如数据库连接失败、空指针未捕获)

生产环境必须记录

WARNING

900

警告,潜在问题但系统仍可运行(如配置缺失使用默认值、重试成功)

需要关注,可能预示未来故障

INFO

800

信息性消息,记录系统关键节点(如启动完成、用户登录、定时任务触发)

生产环境默认开启

CONFIG

700

配置信息,记录初始化参数、环境配置(如端口、数据源URL、线程池大小)

调试部署问题、环境差异

FINE

500

调试信息(基本),方法入口/出口、关键变量值

开发/测试环境

FINER

400

更细粒度调试,循环内部、分支判断、中间状态

详细调试复杂逻辑

FINEST

300

最详细调试,每行关键代码、对象内部状态、网络包内容

深度排查问题,性能开销大

ALL

Integer.MIN_VALUE

记录所有级别,最低“级别”,用于开启全部日志

本地调试、问题复现

数值越小,级别越低,日志越详细
只有当日志级别 ≥ Logger 设置的级别时,才会被记录


2.2 日志级别输出案例

package com.toast.learn.spring.core.logger.jdk;import java.util.logging.Level;
import java.util.logging.Logger;/*** @author toast* @time 2025/9/11* @remark*/
public class LogLevelExample {private static final Logger logger = Logger.getLogger(LogLevelExample.class.getName());public static void main(String[] args) {// 设置日志级别为 INFO(默认通常是 INFO)logger.setLevel(Level.INFO);logger.severe("数据库连接失败!");     	// ✅ 输出(SEVERE >= INFO)logger.warning("缓存未命中,使用默认值"); // ✅ 输出(WARNING >= INFO)logger.info("系统启动完成");          	// ✅ 输出(INFO >= INFO)logger.config("监听端口: 8080");       	// ❌ 不输出(CONFIG=700 < INFO=800)logger.fine("进入 doSomething 方法");  	// ❌ 不输出logger.finest("变量 x = 100");        	// ❌ 不输出}
}

输出结果:

9月 11, 2025 6:18:03 下午 com.toast.learn.spring.core.logger.jdk.LogLevelExample main
严重: 数据库连接失败!
9月 11, 2025 6:18:03 下午 com.toast.learn.spring.core.logger.jdk.LogLevelExample main
警告: 缓存未命中,使用默认值
9月 11, 2025 6:18:03 下午 com.toast.learn.spring.core.logger.jdk.LogLevelExample main
信息: 系统启动完成

三,Handler 日志处理

        JDK 自带的日志系统(java.util.logging, JUL)完全支持将日志输出到文件,甚至可以配置多个输出目标(如同时输出到控制台和文件)、按大小或时间滚动、设置编码等。

        日志的默认输出是控制台,是因为默认配置只启用了 ConsoleHandler。我们可以通过配置 FileHandler 来输出到文件。

package com.toast.learn.spring.core.logger.jdk;import java.io.IOException;
import java.util.logging.*;/*** @author toast* @time 2025/9/11* @remark*/
public class JdkLoggingToFileExample {private static final Logger logger = Logger.getLogger(JdkLoggingToFileExample.class.getName());public static void main(String[] args) {try {// 创建 logs 目录java.nio.file.Files.createDirectories(java.nio.file.Paths.get("./logs"));// 创建 FileHandlerFileHandler fileHandler = new FileHandler("./logs/app.log", 10 * 1024 * 1024, 5, true);fileHandler.setEncoding("UTF-8");fileHandler.setLevel(Level.ALL);// 设置格式SimpleFormatter formatter = new SimpleFormatter() {@Overridepublic String format(LogRecord record) {return String.format("[%1$tF %1$tT] [%2$-7s] %3$s - %4$s%n",record.getMillis(),record.getLevel().getLocalizedName(),record.getLoggerName(),record.getMessage());}};fileHandler.setFormatter(formatter);// 添加到 Logger(建议添加到 root logger)Logger rootLogger = Logger.getLogger("");rootLogger.addHandler(fileHandler);// 可选:关闭父级处理器避免重复(如果不需要控制台输出)// logger.setUseParentHandlers(false);logger.info("This is an info message");logger.warning("This is a warning message");logger.severe("This is a severe (error) message");} catch (IOException e) {e.printStackTrace();}}
}

输出结果-控制台:

9月 11, 2025 6:32:24 下午 com.toast.learn.spring.core.logger.jdk.JdkLoggingToFileExample main
信息: This is an info message
9月 11, 2025 6:32:24 下午 com.toast.learn.spring.core.logger.jdk.JdkLoggingToFileExample main
警告: This is a warning message
9月 11, 2025 6:32:24 下午 com.toast.learn.spring.core.logger.jdk.JdkLoggingToFileExample main
严重: This is a severe (error) message

输出结果-文件:

        可以发现每一个Handler 日志输出处理都有一个独立日志格式属性。上述代码并且设置控制台的日志格式,但是文件的输出设置日志格式。

        JDK 除了提供的控制台,File输出,还提供了MemoryHandler,SocketHandler,StreamHandler,一个是往内存输出,一个是通过网络输出出去,和最后OutputStream 流输出

四,日志格式化

        JDK 自带的日志支持日志自定义格式的。

package com.toast.learn.spring.core.logger.jdk;import java.util.logging.*;/*** @author toast* @time 2025/9/11* @remark*/
public class LogFormatExample {private static final Logger logger = Logger.getLogger(JdkLoggingExample.class.getName());public static void main(String[] args) {// 获取控制台处理器Handler[] handlers = logger.getParent().getHandlers(); // 通常从 root logger 获取for (Handler handler : handlers) {if (handler instanceof ConsoleHandler) {// 创建自定义格式SimpleFormatter formatter = new SimpleFormatter() {@Overridepublic String format(LogRecord record) {return String.format("[%1$tF %1$tT] [%2$-7s] %3$s - %4$s%n",record.getMillis(),record.getLevel().getLocalizedName(),record.getLoggerName(),record.getMessage());}};handler.setFormatter(formatter);}}logger.info("This is an info message");logger.warning("This is a warning message");logger.severe("This is a severe (error) message");}
}

输出内容:

[2025-09-11 18:28:48] [信息     ] com.toast.learn.spring.core.logger.jdk.JdkLoggingExample - This is an info message
[2025-09-11 18:28:48] [警告     ] com.toast.learn.spring.core.logger.jdk.JdkLoggingExample - This is a warning message
[2025-09-11 18:28:48] [严重     ] com.toast.learn.spring.core.logger.jdk.JdkLoggingExample - This is a severe (error) message

五,JDK 日志设置挺全面的,为什么流行不起来

你说:“JDK 自带的日志设置得也并不错,为什么会不流行使用?

✅ 你的观察是对的 —— 从功能上讲,JUL(java.util.logging)确实“不差”

  • 支持级别控制(INFO/WARN/SEVERE…)
  • 支持 Handler(输出到控制台、文件、网络等)
  • 支持 Formatter(自定义格式)
  • 支持 Filter(过滤日志)
  • 支持 ResourceBundle(国际化)
  • 线程安全、模块化支持、配置文件驱动

但“功能完整” ≠ “好用” ≠ “流行”


🚫 为什么 JUL 不流行?—— 7 大核心原因


1️⃣ 🧩 配置复杂、不够灵活(最大痛点)

  • JUL 依赖 logging.properties,语法老旧、不直观。
  • 想按天滚动日志?❌ 不支持,只能按大小。
  • 想输出 JSON 格式?❌ 很难,需自定义 Formatter。
  • 想异步输出避免阻塞?❌ 无内置支持。
  • 想动态修改日志级别(不重启)?❌ 需要自己写代码或依赖 JMX。

🆚 Logback/Log4j2:支持 XML/YAML/JSON 配置、按天/小时滚动、异步日志、热更新、JSON 格式、条件配置等。


2️⃣ 🐢 性能一般,尤其高并发下

  • JUL 的 Handler 默认是同步阻塞写入(如写文件、网络)。
  • 没有内置异步 Appender(Log4j2 有 AsyncAppender,Logback 有 AsyncAppender)。
  • 在微服务、高吞吐场景下,容易成为性能瓶颈。

📊 性能对比(粗略):

  • Log4j2 (异步) > Logback > JUL

3️⃣ 🧱 扩展性差,生态弱

  • 想输出到 Kafka、Elasticsearch、Graylog?❌ JUL 没有官方支持。
  • 想集成 MDC(Mapped Diagnostic Context,如 traceId)?❌ 需要自己封装。
  • 社区几乎没有第三方 Handler/Formatter 插件。

🆚 Logback/Log4j2:海量插件、开箱即用、社区活跃。


4️⃣ 🤝 与主流框架集成差

  • Spring Boot 默认用 Logback,不加载 JUL 的 logging.properties
  • 如果你想在 Spring Boot 里用 JUL,需要手动排除默认日志、桥接、配置 —— 非常麻烦。
  • 大部分开源库(如 Hibernate、Netty、Apache Commons)默认使用 SLF4J —— 如果你用 JUL,需要桥接(jul-to-slf4j),增加复杂度。

✅ SLF4J + Logback 成为事实标准 → JUL 被边缘化。


5️⃣ 📜 API 设计不够友好

JUL 的 API 冗长:

  • java1Loggerlogger = Logger.getLogger(MyClass.class.getName()); // 冗长

vs SLF4J:

  • java1Loggerlogger = LoggerFactory.getLogger(MyClass.class); // 简洁

不支持 {} 占位符(容易造成字符串拼接性能浪费):

  • java1234⌄// JUL:必须手动判断是否启用if (logger.isLoggable(Level.FINE)) { logger.fine("User " + name + " logged in at " + time); // 性能差!}

vs SLF4J:

  • java1logger.debug("User {} logged in at {}", name, time); // 自动判断,无性能损耗

6️⃣ 🌐 历史包袱 & 社区惯性

  • JUL 是 2002 年随 JDK 1.4 推出的,设计较早。
  • 2006 年 Log4j 作者又推出 SLF4J + Logback,设计更现代、性能更好。
  • 2014 年 Log4j2 诞生,性能碾压 JUL。
  • 开发者一旦习惯 SLF4J,就不会回头用 JUL —— 生态一旦形成,很难逆转

📈 趋势:JUL → Log4j → SLF4J + Logback/Log4j2


7️⃣ 🛠 调试和运维不友好

JUL 默认输出格式不统一、不美观:

  • 129月 11, 2025 5:07:41 下午 com.example.MyClass main信息: This is an info message

Logback/Log4j2 默认格式清晰、可读性强:

  • 12025-09-11 17:07:41.123 [main] INFO com.example.MyClass - This is an info message
  • 缺少“开箱即用”的生产级配置(如按天滚动、压缩归档、错误分离等)。

✅ 什么时候适合用 JUL?

虽然不推荐新项目使用,但以下场景 JUL 仍有价值:

JDK 工具/小型工具类

不想引入第三方依赖,JUL 零依赖

Android 开发(早期)

早期 Android 支持 JUL(现在推荐 Timber + SLF4J)

阅读 JDK 源码

JDK 内部大量使用 JUL,理解它有助于阅读源码

维护老项目

有些老系统仍在用 JUL,需要维护


📊 流行度对比(2025 年)

SLF4J + Logback

✅✅✅ 80%+

Spring Boot 默认,生态最成熟

SLF4J + Log4j2

✅✅ 15%

性能最强,适合高并发

JUL (java.util.logging)

❌ <5%

仅用于 JDK 内部或老项目

Log4j 1.x

❌ 已淘汰

安全漏洞,不再推荐

📉 JUL 的使用率逐年下降,尤其在云原生、微服务架构下几乎绝迹。


💡 总结:为什么“功能不差”却不流行?

✍️ “JUL 是一辆功能齐全的老爷车 —— 有方向盘、有刹车、能跑,但没人想开它上高速。”

  • 它诞生太早,设计不够现代。
  • 性能、扩展性、易用性被后起之秀全面超越。
  • 生态和社区惯性让它逐渐边缘化。
http://www.dtcms.com/a/427623.html

相关文章:

  • (基于江协科技)51单片机入门:7.LED点阵屏
  • 江协科技 CAN总线入门课程(错误处理)
  • 网站的建设与规划方案企业网站建设要素
  • antdv- Tooltip 文字提示组件
  • 算法题(222):摆花
  • 如何向alexa提交网站wordpress custom login
  • SpringCloud电商微服务项目衣拉客搭建指南
  • dev c++工具下载 dev c++安装包下载 dev c++软件网盘资源分享
  • 如何去掉Excel多余空行
  • 房地产网站欣赏万网空间管理
  • 做多语言网站多少钱免费网站安全软件大全下载安装
  • 【密码学实战】openHiTLS X509命令行工具: 数字证书生成与转换
  • 从“减塑”到“降碳”新天力“2R”模式推动行业低碳转型
  • AFSim雷达显控一体化
  • 网站建设类型智盈中心网站建设
  • 零基础从头教学Linux(Day 45)
  • 网站策划方案论文wordpress软件网站模板下载
  • 大数据变长存储算法
  • Ubuntu22.04安装Samba服务器
  • NACHI那智焊接机器人智能气阀
  • 网站怎么申请怎么注册交友软件网站建设
  • 网站建设官网多少钱设计公司名字logo
  • 安卓开发---通信录的UI实例
  • 35互联做的网站效果图制作教程
  • Gitee - IDEA 主支 master 和分支 dev 的使用
  • grep 命令处理文件差集
  • MySQL终极备份指南:用Percona XtraBackup实现零数据丢失!
  • FPGA实现SRIO图像视频传输,基于Serial Rapidlo Gen2,提供6套工程源码和技术支持
  • 网站推广渠道有哪些加盟编程教育哪家好
  • GitOps实战:Helm一键部署ArgoCD