java.util.logging.Logger

来源:互联网 发布:古建筑效果图制作软件 编辑:程序博客网 时间:2024/05/21 09:35

java.util.logging.Logger

工作原理

        首先通过LoggerManager进行日志框架的初始化,生成Logger的根节点RootLogger。 这里需要注意的是LoggerManager的初始化工作,并没有将构建配置文件中所有的日志对象,而仅仅是构建了根节点,这种方式就是我们多例模式中经常用到的懒加载,对象只有在真正被时候的时候,再进行构建。 通过Logger.getLogger(String name) 获取一个已有的Logger对象或者是新建一个Logger对象。Logger,日志记录器,这就是在应用程序中需要调用的对象了,通过Logger对象的一系列log方法。


Logger的大致处理流程


        收到应用程序的记录请求,将参数中的日志信息和运行时的信息构建出LogRecord对象,而后通过Logger对象本身设置的记录级别和调用者传递进来的日志级别,如果传递进来的日志级别低于Logger对象本身设置的记录级别(从语义上的理解,而实际上语义级别越高的级别其内部用数字表示的标志的数值越小),那么Logger对象将直接返回,因为他认为这条日志信息,在当前运行环境中,没有必要记录。 

         而满足以上条件的日志信息,将会通过Logger对象的filter元素的过滤校验,filter是动态的,在运行时是可以随意设置的,如果有filter对象,那么将调用filter对象,对日志对象LogRecord进行校验,只有校验通过的LogRecord对象,才会继续往下执行。

         通过filter校验后,Logger对象将依次调用其配置的处理器,通过处理器来真正实现日志的记录功能,一个Logger对象可以配置多个处理器handler,所以一条日志记录可以被多个处理器处理,同时Logger对象的实现是树形结构,如果Logger对象设置其可以继承其父节点的处理器(默认),一条日志记录还会被其父节点的Logger对象处理。  而handler的处理方式就会是形形色色了,但是归根节点,会有以下几个大的步骤:

        1. 级别的判定和比较,决定某条具体的日志记录是否应该继续处理 

        2. 将日志记录做格式化处理,以达到输出的日志在格式上统一,美观,可读性高。 

        3. 资源的释放,不管是以何种方式记录日志,总是会消耗一些方面的资源,所以会涉及到资源的释放问题。比如以文件方式记录的日志的,在一定的时候需要做文件关闭操作,以报文方式发送日志的,在和远程通话的过程中,也需要涉及到网络IO的关闭操作,或者是存储在数据库等等,资源释放在程序开发过程中,是个不变的主题。


创建Logger对象

// 为指定子系统查找或创建一个 logger。static Logger getLogger(String name) // 为指定子系统查找或创建一个 logger。static Logger getLogger(String name, String resourceBundleName)

注意:name是Logger的名称,当名称相同时候,同一个名称的Logger只创建一个。


Logger的级别

        在进行信息的记录时,依信息程序的不同,会设定不同等级的信息输出。Java log比log4j的级别详细,全部定义在java.util.logging.Level里面。各级别按降序排列如下:

  • SEVERE(最高值)
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST(最低值)

        此外,还有一个级别OFF,可用来关闭日志记录,使用级别ALL启用所有消息的日志记录。

        logger默认的级别是INFO,比INFO更低的日志将不显示。

        Logger的默认级别定义是在jre安装目录的lib下面。


可以通过操作Logger上的几个方法来得到不同等级的信息输出。如下:

package com.zxt.base;import java.util.logging.Logger;public class LoggerTest {public static void main(String[] args) {Logger log = Logger.getLogger("lavasoft");log.severe("严重信息");log.warning("警示信息");log.info("一般信息");log.config("设定方面的信息");log.fine("细微的信息");log.finer("更细微的信息");log.finest("最细微的信息");}}

        程序运行的结果如下,此例中config()方法及以下的信息并没有显示出来,这是因为Logger的默认等级是INFO,比这个等级更低的信息,Logger并不会将信息输出。


        Logger默认的输出媒介控制器(Handler)是java.util.logging.ConsolerHandler,也就是将信息输出至控制台。一个Logger可以拥有多个handler,每个handler可以有自己的日志级别,在通过Logger的级别限制后,实际上还要再经过handler的级别限制。所以如果想要看到所有的信息,则必须同时设定Logger与ConsoleHandler的级别。示例如下:

package com.zxt.base;import java.util.logging.ConsoleHandler;import java.util.logging.Level;import java.util.logging.Logger;public class LoggerTest {public static void main(String[] args) {Logger log = Logger.getLogger("lavasoft");// 显示所有等级的信息log.setLevel(Level.ALL);// 输出媒介(控制台)的级别设定ConsoleHandler consoleHandler = new ConsoleHandler();consoleHandler.setLevel(Level.ALL);// 设定Logger的的Handler为ConsoleHandlerlog.addHandler(consoleHandler);log.severe("严重信息");log.warning("警示信息");log.info("一般信息");log.config("设定方面的信息");log.fine("细微的信息");log.finer("更细微的信息");log.finest("最细微的信息");}}

程序输出结果:


        Level.ALL表示显示所有的信息,所有这一次的执行结果可显示所有等级的信息。如果要关闭所有的信息,可以设定为Level.OFF。

        Logger的Severe(),warning(),info()等方法,实际上是个便捷的方法。也可以直接使用log()方法并指定等级来执行相同的作用,如:

logger.log(Level.SEVERE, "严重信息test");


Handler

        Handler对象从Logger中获取日志信息,并将这些信息导出。例如,它可将这些信息写入控制台或文件中,也可以将这些信息发送到网络日志服务中,或将其转发到操作系统日志中。

        Logger默认的输出处理者是ConsoleHandler。ConsoleHandler的输出是使用System.err对象,而信息的默认等级是INFO,这可以在JRE安装目录下lib目录的logging.properties中看到。

       Java SE实现了5个Handler:

        1)    java.util.logging.ConsoleHandler 以System.err输出日志。

        2)    java.util.logging.FileHandler 将信息输出到文件。

        3)    java.util.logging.StreamHandler以指定的OutputStream实例输出日志。

        4)    java.util.logging.SocketHandler将信息通过Socket传送至远程主机。

        5)    java.util.logging.MemoryHandler将信息暂存在内存中。


例如可以将日志信息输出到文件中:

package com.zxt.base;import java.io.IOException;import java.util.logging.FileHandler;import java.util.logging.Logger;public class LoggerTest2 {public static void main(String[] args) {Logger logger = Logger.getLogger("LoggerTest2");try {FileHandler fileHandler = new FileHandler("log.txt");logger.addHandler(fileHandler);logger.info("测试信息");} catch(SecurityException ex) {ex.printStackTrace();} catch(IOException ex) {ex.printStackTrace();}}}

执行程序,得到log.txt文件:

<?xmlversion="1.0" encoding="GBK" standalone="no"?><!DOCTYPE log SYSTEM"logger.dtd"><log><record> <date>2017-04-07T19:01:28</date>  <millis>1491562888114</millis> <sequence>0</sequence> <logger>LoggerTest2</logger> <level>INFO</level> <class>com.zxt.base.LoggerTest2</class> <method>main</method> <thread>1</thread> <message>测试信息</message></record></log>

        fileHandler默认的输出格式是XML格式。输出格式由java.util.logging.Formatter来控制。


Formatter

        Formatter为格式化LogRecords提供支持。一般来说,每个Handler都有关联的Formatter。Formatter接受LogRecord,并将它转换为一个字符串。

        默认提供了两种Formatter:

        1.java.util.logging.SimpleFormatter:标准日志格式,就是我们通常在启动一些诸如 Tomcat JBoss之类的服务器的时候经常能在控制台下看到的那种形式。

        2.java.util.logging.XMLFormatter:XML形式的日志格式,如果为Logger添加了一个newXMLFormatter(),那么就会以XML形式输出,不过更常用的是使用上面介绍的FileHandler输出到XML文件中。

        FileHandler的默认格式是java.util.logging.XMLFormatter,而ConsolerHandler的默认格式是java.util.logging.SimpleFormatter,可以使用Handler实例的setFormatter()方法来设定信息的输出格式。例如:

fileHandler.setFormatter(new SimpleFormatter());

        FileHandler的Formatter设定为SimpleFormatter,则输出的日志文件内容就是简单的文字信息,打开文件后会发现与命令行模式下看到的信息内容相同。


自定义

Handler

        用户可以定制自己输出媒介控制器,继承Handler即可,通常只需要实现Handler中三个未定义的抽象方法:

        1.    publish:主要方法,把日志记录写入你需要的媒介。

        2.    flush:清除缓冲区并保存数据。

        3.    close:关闭控制器。

        通过重写以上三个方法可以很容易实现一个新的输出媒介控制器。

Formatter

        除了XMLFormatter与SimpleFormatter之外,也可以自定义日志的输出格式,只要继承抽象类Formatter,并重新定义其format()方法即可。format()方法会传入一个java.util.logging.LogRecord对象作为参数,可以使用它来取得一些与程序执行有关的信息。

package com.zxt.base;import java.io.IOException;import java.util.logging.FileHandler;import java.util.logging.Formatter;import java.util.logging.LogRecord;import java.util.logging.Logger;public class LoggerTest3 {public static void main(String[] args) {Logger logger = Logger.getLogger("LoggerTest3");try {FileHandler fileHandler = new FileHandler("mylog.txt");// 设置日志输出的格式fileHandler.setFormatter(new MyLogHander());logger.addHandler(fileHandler);logger.info("自定义输出格式测试");logger.info("自定义输出格式测试");} catch (SecurityException ex) {ex.printStackTrace();} catch (IOException ex) {ex.printStackTrace();}}}class MyLogHander extends Formatter {@Overridepublic String format(LogRecord record) {return record.getLevel() + ": " + record.getMessage() + "\n";}}

输出的日志信息如下:

INFO: 自定义输出格式测试INFO: 自定义输出格式测试

日志级别

        Java的一个日志级别对应一个整数值,Level有9个内置的级别,分别是:

类型

对应的整数

OFF

最大整数( Integer. MAX_VALUE)

SEVERE

1000

WARNING

900

INFO

800

CONFIG

700

FINE

500

FINER

400

FINEST

300

ALL

最小整数(Integer. MIN_VALUE)

        你也可以定义自己的日志级别,通过继承Level的方式,譬如:

package com.zxt.base;import java.util.logging.Level;import java.util.logging.Logger;public class LoggerTest4 extends Level{private static final long serialVersionUID = -8126219091687885667L;protected LoggerTest4(String name, int value) {super(name, value);}public static void main(String[] args) {Logger logger = Logger.getLogger("LoggerTest4");// INFO的级别数为800,比800低不显示logger.log(new LoggerTest4("newLevelInfo",950), "自定义的LEvel");}}

Logger的层次关系

        在使用Logger的静态getLogger()方法取得Logger实例时,给getLogger()方法的名称是有意义的。如果给定a,实际上将从根(Root)logger继承一些特性,像日志级别(Level)以及根logger的输出媒介控制器。如果再取得一个Logger实例,并给定名称a.b,则这次取得的Logger将继承名为a的这个Logger上的特性。示例:

package com.zxt.base;import java.util.logging.Level;import java.util.logging.Logger;public class LoggerTest5 {public static void main(String[] args) {Logger loggera = Logger.getLogger("a");Logger loggerab = Logger.getLogger("a.b");System.out.println("loggera's parent: " + loggera.getParent());System.out.println("loggerab's parent: " + loggerab.getParent().getName());System.out.println("loggerab's name: " + loggerab.getName());loggera.setLevel(Level.WARNING);loggerab.info("loggerab's info");loggerab.setLevel(Level.INFO);loggerab.info("loggerab's info");}}

        

        getParent()方法可以取得Logger上的上层父Logger,根Logger并没有名称,所以直接调用它的toString()以取得字符串描述。当Logger没有设定等级时,则使用父Logger的等级设定,所以在上面的范例中,loggera设定等级为WARNING时,loggerab调用info()方法时并不会有信息显示(因为WARNING等级比INFO高)。

1 0
原创粉丝点击