java 日志_Java原生日志(下)

14ecda304117f0a1fc3fe73702d748b1.png

原生

1 Handler使用和设置

虽然从使用上来说Logger可以单独使用,但并不代表Logger能够独立完成日志的工作。日志的处理工作有两大部分组成,第一个部分是日志的记录工作,这时Logger的主要工作,它主要的作用是将外界信息转化成日志信息并进行初步的管理,我们可以理解为日志信息的收集和创建。第二个部分是日志处理工作,日志处理工作主要对Logger中已经记录的信息进行处理导出,只有通过导出后,我们才可以获取日志信息,我们可以理解为日志信息的处理和发送。在日志体系中Logger和Handler是一种聚合关系,一个Logger中可以包含一个或者多个Handler。

Handler处理的功能范围包括日志信息的编码、格式、过滤、以及设置日志级别。Handler本身不能设置日志导出的目的地,也没有提供相关的方法进行设置,Handler本身还是一个抽象类,必须使用Handler的子类来完成。

Handler子类对日志的导出分类两种类型,一种向内存缓冲导出,用MemoryHandler类来实现。另一种是IO导出,IO导出由下面三个类来完成:

· ConsoleHandler:可以将日志信息发布到控制台上,由System.err输出。

· FileHandler:可以将日志信息输出到文件中,也可以写入到文件轮换集中。

· SocketHandler:将日志信息发布网络连接中。

如果我们想要将日志信息导出到文件中,我们只能选择FileHandler来完成。在创建FileHandler时候有五个构造方法供我们选择:

1.FileHandler()

2.FileHandler(String pattern)

3.FileHandler(String pattern, boolean append)

4.FileHandler(String pattern, int limit, int count)

5.FileHandler(String pattern, int limit, int count, boolean append)

在这些构造方法中,patter是用字符串表示的文件路径,append用于表示文件是否为追加模式。limit表示日志文件最大的字节数,count表示日志文件的个数。使用不同构造方法创建的日志处理器,它们的功能各不相同。如果我们没有设置append参数,它默认为覆盖模式,这样会造成就日志的覆盖。所以如果我们想让日志处理追加状态,需要使用第3或者第5个构造方法,并将append参数设置为true。

在一些生产环境中,系统日志记录的数量非常多,这样会造成日志文件过于庞大,如果日志过大,日志是不利于查看,甚至无法用普通工具打开查看。这时我们需要构造方法中的第4和第5个,可以将日志信息导出到文件集中,当日志文件的字节数超过limit时,日志文件会重新建立一个新的文件,并向新的文件中写入,例如我们指定的日志文件为"log.log",在导出文件集时,会默认将第一个日志文件修改为"log.log.0"以此类推,最大为count。当FileHandler导出日志文件集的数量超过了count时,再次导出的日志会从第1个日志文件开始写起,并将原有的日志信息进行了覆盖,并会一直重复这个过程。

在下面示例中,我们演示了FileHandler和Logger结合后,将日志信息输出到文件中。

685e20587e968227d6ca6e8701f7384b.png

在上述示例中,我们创建的文件处理器,可以将日志信息导出到文件集中。在运行程序示例后,我们依然可以看见控制台上日志的打印情况。但在文件所在位置上,我们可以看到创建了新的文件"log.log.0",我们使用文件查看工具打开后,日志文件内容如下图所示。

88ef039140e14365c94642b684b62f59.png

日志文件log.log.0

从日志文件内容中,我们可以发现,文件中的日志信息与控制台打印的信息格式不同,但内容更丰富。这是由于FileHandler采用的默认格式为XMLFormatter。

Java的日志体系中,提供两种基础日志格式:一种用XMLFormatter类进行封装XML格式,XML是一种扩展标记格式。在XML格式下,每一条日志用一对""标签表示。标签内出现的每一对子标签都表示日志中包含的信息名称,标签中的文本表示信息的内容。我们可以找到日志的时间、等级、所在方法、以及Logger名称等详细的日志信息。

另一种是普通信息,这也是控制台所打印出来的信息,以SimpleFormatter类进行装封的简单基本信息。如果我们想让日志文件中的格式与控制台格式保持一致。我们可以通过Handler的setFormatter方法进行设置,如"setFormatter(new SimpleFormatter())"。

如果我们想构造出更复杂的格式,我们可以自己创建格式类,但必须继承Formatter抽象类(java.util.logging.Formatter,它是XMLFormatter和SimpleFormatter的父类)。

在记录日志的时候,我们可以通过Logger的 setLevel方法设置日志消息级别。在Handler中同样存在setLevel方法,并且中默认的日志级别也为info。

如果Logger和Handler中都设置了日志级别,Handler会被Logger所影响。被Logger所忽略掉的日志信息,Handler是无法导出的(Logger会先过滤符合级别的日志)。在下面示例中,我们演示了这一个过程。

ebb4e7ebafafc11c2364d9aaea265a6f.png

在上述示例中,FileHandler的日志级别低于Logger,在运行程序示例时,我们发现控制台只输出了WARNING级别的日志。在导出的日志文件中,我们也只发现了WARNING级别的日志信息。日志文件"log.log"信息如下图所示。

db462720175657abf606ac847640fcac.png

日志文件log.log

如果FileHandler中设置的日志级别高于Logger中的级别,这样可以实现双层日志级别过滤。

2 Logger中的Handler

Logger作为日志的记录器,拥有记录日志的功能,Handler是日志的处理器,主要功能是设置日志的格式,并将日志导出。

每一个Logger可以包含一个或者多个Handler,这样一个Logger就可以同时向多个文件、多个网络端以及控制台导出日志信息。如果Logger不包含日志处理器,日志记录器中记录的日志就无法被导出,外界无法观察到日志信息。

在默认的 Logger中,当我们将信息交由Logger记录时,默认会从控制台进行输出。这说明Logger中应该还有默认的Handler,并且是一个ConsoleHandler类型的处理器。在Logger中与Handler相关的方法有五个,如下表所示。

Logger中与Handler相关的方法

除了Handler的添加和删除方法外,getHandlers方法可以获得当前Logger中所有绑定的记录器,这样我们可以通过该方法获取并修改Logger中的Handler。剩下的两个方法涉及到父记录器中的Handler(在Logger日志体系中,每一个Logger对象都会有一个父Logger,如果我们想深入了解原因,可以参考LoggerManager中的说明)。

当我们创建了Logger时,无论我们是否给新建的Logger添加Handler,它都会继承父记录器的消息级别,并且会将日志消息发送给父记录器。父记录器中的Handler默认是一个ConsoleHandler类型的Handler,所以当我们创建的Logger在记录日志后,都会输出到了控制台。在没有停止使用父记录器的Handler时,getUseParentHandlers方法永远返回true。同样的,我们可以调用setUseParentHandlers方法,停止使用父记录器Handler。

由于父Handler的存在,在一些情况下,它会给我们造成一些错觉,当我们讲Logger的消息权限设置在INFO以下的级别时,我们会发现setLevel方法无效,控制台上无法显示INFO级别以下的消息,造成这个原因并非setLevel方法无效,而是父Logger中的Handler默认级别为INFO,它会再次过滤一次。如果要解决这个问题,最直接的方法有两种,一种是直接修改父Logger中的Handler,另外一种方法为停止使用父Handler,给当前Logger添加一个消息级别的Handler,在下面程序示例中,我们演示了这两种方法。

118aa73db096e1138d4cd33b39bd9d9e.png
e7bb332ddab064be0ceea6d605fce791.png
1ccbdb4b71e77df2795f583dcb57e47e.png

在logging方法中记录了三个等级的日志信息,其中最低级的消息为CONFIG消息。其它三个设置方法用来设置Logger的记录消息的级别以及Handler处理消息的级别,其中defaultHandler方法中没有设置任何Handler,直接设置了消息记录的级别。在mian方法中,我们对比了三种设置,对日志记录以及处理的影响。

b995ffe39001305ce06be29295f3ea7d.png

通过运行结果我们可以观察到,当只设置Logger记录日志等级时,INFO以下的信息是无法正常的被输出到控制台,而另外两种设置方法均可。

但要注意,所有新创建的Logger在没有指定父Logger时,默认的父Logger是一个没有名称的Logger,它是所有Logger的父Logger。这意味着,只要我们修改了它的Handler级别,其它Logger都会受到影响,所以我们尽量使用useDiyHandler中的设置方法。

3 过滤器Filter和LogRecord

设置日志管理级别(Logger级别)和日志处理级别(Handler)虽然一定程度上可以对日志信息进行过滤,但是如果要想加入更为精确的信息过滤,就需要使用到日志的过滤器。日志过滤器可以过滤敏感词汇。

在Logger和Handler中都可以进行过滤器Filter的设置,它们在各自的环节中起到的作用是一样的。

Filter是一个接口,在Java中并没有提供实现类,要实现过滤器功能,需要我们自己实现,这一点与File中的名称过滤器是相似的。接口Filter位与java.util.logging包中,该接口只有一个抽象方法用于日志的过滤的判断:

boolean isLoggable(LogRecord record):当方法返回true时,表示日志可以被记录或者被发布。

isLoggable 方法的参数LogRecord是一个真正的日志消息对象,它封装了日志消息的所有信息,并提供了修改方法。我们在使用Logger记录日志消息的时候,建议使用log(LogRecord record)方法。无论任何级别、带有错误的消息,都可以封装在LogRecord对象中。在Logger类中,所有的消息添加,最终都有该方法完成。在下面程序示例中,我们先演示LogRecord的使用。

1dc5d39f53521e9cb2ad154917e00e41.png

LogRecord只有一个构造方法,需要提供日志的消息等级,以及日志的信息。如果要将异常信息封装到LogRecord对象中,我们还需要额外使用setThrown来设置异常信息(行19处)。如果要设置其它信息,我们可以查询它的API获取相关方法。

b96730f4f322eceb4a23e5f902a39fa0.png

在Logger记录日志的时候,Logger提供了多种日志等级记录的方式以及log记录方式,其本质上都是创建了LogRecord,然后在进行记录。在使用过滤器的时候,即时我们没有创建过LogRecord对象,但Logger内部依然会将我们提供的信息封装成LogRecord。当我们在使用Filter进行过滤的时候,不用特意将日志信息用LogRecord的方式进行创建。

Filter的主要功能是同等级日志信息的基础上设定一套过滤规则,例如在同等级的日志信息中,有些包含特殊关键词的日志是我们特别关心的,我们就可以通过过滤器来实现。在下面程序示例中,我们通过Filter指定了过滤规则。

5866713d8caabccbdfac889ecda7a763.png
49ff7cf3233d51ab9216e67ffeee3c56.png

Filter的实现类DiyFilter会对日志信息的内容进行判断,当出现敏感词时keyWord,才允许日志被处理发布。在main方法中,我们设定的敏感词为"insert"。在日志记录器记录的日志中,只有出现了"insert"关键词的日志才会被输出到控制台上。

e95f064ae531097affc657a55375b6c3.png

在日志信息中,异常信息是级别较高的信息,但运行时异常的出现,这就需要我们额外的关注,我们也可以通过过滤器来实现。在下面程序示例中,我们对异常信息进行了过滤。

348d53d5ed30ec4b3326912d59f6a610.png
6bcd8c66272ffd6f75a8acf7224cd080.png
cee90bbaaa58433b0b06ead33527f1f5.png

类ExceptionFilter为过滤异常指定了规则,只有RuntimeException类型的异常才能被允许处理,从LogRecord中获取异常对象可以使用方法getThrown。如果没有异常,该方法返回null。

在main方法中,我们创建了两个Handler,一个Handler用于向控制台无差别输出,另一个Handler使用ExceptionFilter过滤器,将RunTimeException类型的异常输出到文件中。在for循环中,我们设计了几种日志记录的方式,其中ConsoleHandler会将日志信息全部在控制台中输出:

2c8ff0fb6de2d4370c53e8b608a7626b.png

另外一个FileHandler,会将过滤后的日志输出在"h:unException.log"文件中,该文件内容如下图所示。

27d3b4dfb970a101fb4c4c40766d0c90.png

FileHandler输出过滤后的日志

关于Java的原生日志,我们就谈到这里。在实际的开发中,有很多框架都自带日志功能,但多数也是构建在Java原生日志之上的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值