闲人细嚼慢咽啃Netty:java.util.logging到底是什么?

参考网址

Java™日志记录技术

java.util.logging 包中引入的 Java™Logging API 通过生成适合最终用户,系统管理员,现场服务工程师和软件开发团队分析的日志报告,促进客户站点的软件服务和维护。Logging API捕获应用程序或平台中的安全性故障,配置错误,性能瓶颈和/或错误等信息。

核心软件包包括支持将纯文本或XML格式的日志记录传送到内存,输出流,控制台,文件和套接字。此外,日志记录API能够与主机操作系统上已存在的日志记录服务进行交互。

1.1 控制流程概述

应用程序调用 Logger 对象的方法来打印日志。Loggers 以命名空间(namespace)的层级关系来组织,子记录器可以从命名空间中的父项继承一些记录属性。

应用程序在 Logger 对象上进行日志调用。这些Logger对象分配 LogRecord 对象,这些对象传递给 Handler 对象以供 发布。Logger 和 Handler 都可以使用日志记录级别和过滤器(可选的)来确定他们是否对特定的 LogRecord 感兴趣 。当需要在外部发布 LogRecord 时,处理程序可以(可选的)使用 Formatter 在将消息发布到I / O流之前对消息进行本地化和格式化。

java util logging

每个 Logger 都会跟踪一组输出处理程序。默认情况下,所有 Logger 也将其输出发送到其父 Logger。但 Logger 也可能被配置为忽略树上方的处理程序。

看了半天没有看懂,说的啥?上面的图有误解的地方,画Handler那里其实想表示的是一组Handler.  而每一个handler只有一个Filter. 一个Logger包含了一组handlers

再上一张图装一下:
日志结构
我的理解是:Application 会使用 Logger提供的方法来打印日志。而日志的打印控制,可以通过LoggerLevel的日志级别来控制。比如我设置了DEBUG级别的日志,那么就可以打印debug级别的日志了。但是,这种控制太宽泛了,比如我有三个package. 一个是com.huyouxiao.service. 一个是com.huyouxiao.mapper. 一个是com.huyouxiao.controller. 如果我只想看service包下面的debug日志,其他的debug都不想看。怎么办?就需要提供更加细粒度的控制。刚好接口Filter就定一个接口来判断要不要打日志。而控制是根据日志name里面的包路径来的吗?我感觉是。难怪每次初始化日志名,都是 XXXX.class.

那日志答应到哪里去呢?可以达到控制台 (ConsoleHandler). 也可以打印到文件里面 (FileHandler). 还可以通过套接字通过网路打到服务器上去 (SocketHandler). 还可以先在内存里面缓存一下 (MemoryHandler). 我废话了这么多,下面有标准讲解。

@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);
}
ConsoleHandler

参考这里

此Handler将日志记录发布到System.err。默认情况下,SimpleFormatter用于生成简短摘要。
配置: 默认情况下,使用以下LogManager配置属性初始化 每个ConsoleHandler。如果未定义属性(或具有无效值),则使用指定的默认值。

java.util.logging.ConsoleHandler.level 指定了默认级别处理程序 (默认为Level.INFO)。
java.util.logging.ConsoleHandler.filter 指定要使用的Filter类的名称(默认为无Filter)。
java.util.logging.ConsoleHandler.formatter 指定要使用的Formatter类的名称(默认java.util.logging.SimpleFormatter)。
java.util.logging.ConsoleHandler.encoding 要使用的字符集编码的名称(默认为默认平台编码)。
MemoryHandler

参考这里:

处理程序在内存中的循环缓冲区中缓冲请求。
通常,此Handler只是将传入的LogRecords存储 到其内存缓冲区中,并丢弃先前的记录。这种缓冲非常便宜并且避免了格式化成本。在某些触发条件下,MemoryHandler会将其当前缓冲区内容推送到目标Handler,后者通常会将它们发布到外部世界。
触发缓冲区有三种主要模型:
传入的LogRecord的类型大于预定义的级别pushLevel。
外部类显式调用push方法。
子类重写log方法并扫描每个传入的 LogRecord,如果记录符合某些所需条件,则调用push。
配置: 默认情况下,使用以下LogManager配置属性初始化每个MemoryHandler。如果未定义属性(或具有无效值),则使用指定的默认值。如果未定义默认值,则抛出RuntimeException。

    java.util.logging.MemoryHandler.level 指定处理程序的级别 (默认为Level.ALL)。
    java.util.logging.MemoryHandler.filter 指定要使用的Filter类的名称(默认为无Filter)。
    java.util.logging.MemoryHandler.size 定义缓冲区大小(默认为1000)。
    java.util.logging.MemoryHandler.push 定义了pushLevel(默认为level.SEVERE)。
    java.util.logging.MemoryHandler.target 指定目标Handler类的名称。(没有默认值)。

logging through Memory Handler
API的结构使得在禁用日志记录时对Logger API的调用可以很便宜。如果对给定的日志级别禁用日志记录,则Logger可以进行廉价的比较测试并返回。如果为给定的日志级别启用了日志记录,则在将LogRecord传递给处理程序之前,Logger仍会小心地将成本降至最低。特别是,本地化和格式化(相对昂贵)将推迟到Handler请求它们。例如,MemoryHandler可以维护LogRecords的循环缓冲区,而无需支付格式化成本。

1.2 日志级别

每条日志消息都有一个关联的日志级别。该级别粗略指导了日志消息的重要性和紧迫性。日志级别对象封装整数值,值越高表示优先级越高。

该级别类定义了七个标准日志级别,范围从FINEST(最低优先级,最低值)到 SEVERE(最高优先级,最高值)。

1.3 Loggers

如前所述,客户端代码将日志请求发送到Logger对象。每个logger都会跟踪它感兴趣的日志级别,并丢弃低于此级别的日志请求。

logger通常是命名实体,使用点分隔名称,例如“java.awt”。命名空间是分层的,由LogManager管理。命名空间通常应与Java打包命名空间对齐,但不要求盲目地遵循它。例如,名为“java.awt”的Logger可能会处理java.awt包中的类的日志记录请求,但它也可能处理sun.awt中的类的日志记录,这些类支持java.awt包中定义的客户端可见抽象。 。

除了命名的Loggers之外,还可以创建未出现在共享命名空间中的匿名Logger。见1.14节。

logger 会在日志记录命名空间中跟踪其父记录器。logger 的父级是其在日志记录命名空间中最近的现存祖先。根记录器(名为“”)没有父级。匿名记录器都被赋予根记录器作为其父记录。记录器可以在记录器名称空间中从其父项继承各种属性。特别是,记录器可以继承:

  • 日志级别(Logging level)。如果Logger的级别设置为null,则Logger将使用通过向上走父树并使用第一个非null级别获得的有效级别。
  • 处理程序(Handlers)。默认情况下,Logger会将任何输出消息记录到其父级的处理程序,依此向上递增树。
  • 资源包名称(Resource bundle names)。如果记录器具有空资源包名称,则它将继承为其父级定义的任何资源包名称,依此递增树。

Logger类提供了一组用于生成日志消息的便捷方法。为方便起见,每个日志记录级别都有方法,以日志记录级别名称命名。因此,而不是调用“logger.log(Level.WARNING,…”)开发人员可以简单地调用方便方法“logger.warning(…”

有两种不同风格的日志记录方法,可以满足不同社区用户的需求。

首先,有一些方法采用显式的源类名和源方法名。这些方法适用于希望能够快速找到任何给定日志消息源的开发人员。这种风格的一个例子是:

void warning(String sourceClass,String sourceMethod,String msg);
其次,有一组方法不采用显式的源类或源方法名称。这些适用于需要易于使用的日志记录且不需要详细源信息的开发人员。

void warning(String msg);
对于第二组方法,Logging框架将“尽最大努力”确定调用日志框架的类和方法,并将此信息添加到LogRecord中。但是,重要的是要意识到这种自动推断的信息可能只是近似的。最新一代的虚拟机在JITing时执行广泛的优化,并且可能完全删除堆栈帧,从而无法可靠地定位调用类和方法。

1.5 处理程序

Java SE提供以下处理程序:

StreamHandler:用于将格式化记录写入OutputStream的简单处理程序。
ConsoleHandler:用于将格式化记录写入System.err的简单处理程序
FileHandler:将格式化日志记录写入单个文件或一组旋转日志文件的处理程序。
SocketHandler:将格式化日志记录写入远程TCP端口的处理程序。
MemoryHandler:缓冲内存中日志记录的处理程序。
开发新的处理程序非常简单。需要特定功能的开发人员可以从头开发Handler,也可以创建一个提供的Handler子类。

1.6 格式化程序

Java SE还包括两个标准格式化程序:

  • SimpleFormatter:写简短的“人类可读”日志记录摘要。
  • XMLFormatter:写入详细的XML结构信息。
    与处理程序一样,开发新的Formatters也相当简单。
1.7 LogManager

有一个全局LogManager对象可以跟踪全局日志记录信息。这包括:

  • 命名Loggers的分层命名空间。
  • 从配置文件中读取的一组日志记录控制属性。见1.8节。
    可以使用静态LogManager.getLogManager方法检索单个LogManager对象。这是在LogManager初始化期间根据系统属性创建的。此属性允许容器应用程序(例如EJB容器)替换它们自己的LogManager子类来代替默认类。
1.8 配置文件

可以使用将在启动时读取的日志记录配置文件初始化日志记录配置。此日志记录配置文件采用标准java.util.Properties格式。

或者,可以通过指定可用于读取初始化属性的类来初始化日志记录配置。此机制允许从任意源(如LDAP,JDBC等)读取配置数据。有关详细信息,请参阅LogManager API规范。

有一小组全局配置信息。这在LogManager类的描述中指定,并包括要在启动期间安装的根级处理程序的列表。

初始配置可以指定特定记录器的级别。这些级别将应用于命名层次结构中的命名记录器及其下方的任何记录器。级别按照在配置文件中定义的顺序应用。

初始配置可能包含供处理程序或执行日志记录的子系统使用的任意属性。按照惯例,这些属性应使用以处理程序类的名称开头的名称或子系统的主Logger的名称。

例如,MemoryHandler使用属性“java.util.logging.MemoryHandler.size”来确定其环形缓冲区的默认大小。

1.9 默认配置

JRE附带的默认日志记录配置只是默认配置,可以由 ISV,系统管理员和最终用户覆盖。

默认配置仅限制使用磁盘空间。它不会向用户提供信息,但确保始终捕获关键故障信息。

默认配置在根记录器上建立单个处理程序,用于将输出发送到控制台。

1.10 动态配置更新

程序员可以通过多种方式在运行时更新日志记录配置:

  • FileHandlers,MemoryHandlers 和 PrintHandlers 都可以使用各种属性创建。
  • 可以添加新处理程序并删除旧处理程序。
  • 可以创建新的 Loggers,并且可以使用特定的处理程序提供。
  • 可以在目标处理程序上设置级别。
1.11 原生方法

没有用于日志记录的本机API。
希望使用Java Logging机制的本机代码应该对Java Logging API进行正常的JNI调用。

1.12 XML DTD

XMLFormatter使用的XML DTD在附录A中指定。

DTD设计为“”元素作为顶级文档。然后将各个日志记录写为“”元素。
请注意,在JVM崩溃的情况下,可能无法使用适当的结束</ log>干净地终止XMLFormatter流。因此,应准备分析日志记录的工具以应对未终止的流。

1.13 唯一消息ID

Java Logging API不提供对唯一消息ID的任何直接支持。那些需要唯一消息ID的应用程序或子系统应该定义它们自己的约定,并在适当的时候在消息字符串中包含唯一的ID。

1.14 安全

主要安全性要求是不受信任的代码不应该能够更改日志记录配置。具体来说,如果已将日志记录配置设置为将特定类别的信息记录到特定处理程序,则不受信任的代码不应该能够阻止或中断该日志记录。

定义了新的安全权限 LoggingPermission 以控制对日志记录配置的更新。

为受信任的应用程序提供适当的 LoggingPermission,以便它们可以调用任何日志记录配置API。不受信任的applet是另一回事。不受信任的applet可以以正常方式创建和使用命名记录器,但不允许它们更改日志记录控制设置,例如添加或删除处理程序或更改日志级别。但是,不受信任的applet能够使用 Logger.getAnonymousLogger 创建和使用自己的“匿名”记录器。这些匿名记录器未在全局命名空间中注册,并且它们的方法未经过访问检查,甚至允许不受信任的代码更改其日志记录控制设置。

日志记录框架不会尝试防止欺骗。无法可靠地确定日志记录调用的来源,因此当发布声称来自特定源类和源方法的 LogRecord 时,它可能是一种制造。类似地,格式化程序(如XMLFormatter)不会尝试保护自己免受消息字符串中的嵌套日志消息的影响。因此,欺骗性 LogRecord 可能在其消息字符串中包含一组欺骗性XML,使其看起来好像在输出中有另外的XML记录。

此外,日志记录框架不会尝试保护自己免受拒绝服务攻击。任何给定的日志记录客户端都可以使用无意义的消息充斥日志记录框架,以试图隐藏一些重要的日志消息。

1.15 配置管理

API的结构使得初始配置信息集从配置文件中读取为属性。然后可以通过对各种日志记录类和对象的调用以编程方式改变配置信息。

此外,LogManager上还有一些方法可以重新读取配置文件。发生这种情况时,配置文件值将覆盖以编程方式进行的任何更改。

1.16 package

所有日志记录类都在java.util.logging包中的java。*部分名称空间中。

1.17 本地化

可能需要本地化日志消息。

每个Logger可能都有一个与之关联的资源包名称。相应的Resource Bundle可用于在原始消息字符串和本地化消息字符串之间进行映射。

通常,本地化将由Formatters执行。为方便起见,formatter类提供了一个formatMessage方法,该方法提供了一些基本的本地化和格式化支持。

1.18 远程访问和序列化

与大多数Java平台API一样,日志记录API设计用于单个地址空间。所有电话都是本地电话。但是,预计某些处理程序会将其输出转发到其他系统。有多种方法可以做到这一点:

某些处理程序(例如SocketHandler)可能使用XMLFormatter将数据写入其他系统。这提供了一种简单,标准,互换的格式,可以在各种系统上进行解析和处理。

某些处理程序可能希望通过RMI传递LogRecord对象。因此,LogRecord类是可序列化的。但是,如何处理LogRecord参数存在问题。某些参数可能无法序列化,并且其他参数可能已设计为序列化比记录所需的更多状态。为了避免这些问题,LogRecord类有一个自定义的writeObject方法,该方法在将参数写出之前将参数转换为字符串(使用Object.toString())。有关详细信息,请参阅LogRecord API规范。

大多数日志记录类都不是可序列化的。Logger和Handler都是绑定到特定虚拟机的有状态类。在这方面,它们类似于java.io类,它们也不可序列化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值