JUL日志框架的基本使用和运行流程

JUL日志框架的基本使用和运行流程

JUL全称Java util Logging是java原生的日志框架,使用时不需要另外引用第三方类库,相对其他日志框架使用方便,学习简单,能够在小型应用中灵活使用。

在JUL中有以下组件:

  • Loggers:被称为记录器,应用程序通过获取Logger对象,调用其API来发布日志信息。Logger 通常时应用程序访问日志系统的入口程序。
  • Appenders:也被称为Handlers,每个Logger都会关联一组Handlers,Logger会将日志交给关联 Handlers处理,由Handlers负责将日志做记录。Handlers在此是一个抽象,其具体的实现决定了 日志记录的位置可以是控制台、文件、网络上的其他日志服务或操作系统日志等。
  • Layouts:也被称为Formatters,它负责对日志事件中的数据进行转换和格式化。Layouts决定了 数据在一条日志记录中的最终形式。
  • Level:每条日志消息都有一个关联的日志级别。该级别粗略指导了日志消息的重要性和紧迫,我 可以将Level和Loggers,Appenders做关联以便于我们过滤消息。
  • Filters:过滤器,根据需要定制哪些信息会被记录,哪些信息会被放过。

总结一下就是:

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

日志的级别

java.util.log.Level中定义了日志的级别,severe,warning,info,config,fine,finer,finest,off,all

@Test
public void testLogger() {
    Logger logger = Logger.getLogger(LoggerTest.class.getName());
    logger.severe("severe");
    logger.warning("warning");
    logger.info("info");
    logger.config("config");
    logger.fine("fine");
    logger.finer("finer");
    logger.finest("finest");
}

发现只能打印出info及以上的日志,为什么呢?

我们调用是jdk提供的Logger,jdk会默认读取一个对应的配置文件logging.properties,在jdk1.8中这文件的位置是在jdk目录下/jre/lib/loggingproperties下,删除注释后我们可以看到配置文件是


handlers= java.util.logging.ConsoleHandler
.level= INFO

java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

这里.level和consoleHandler的日志级别都是info,这导致上面的logger只能打印info及以上的信息。那么为什么是这样呢?

日志的继承关系

和装饰模式类似,logger实例上可以装载handler,filter,logger和handler都可以调整可打印的日志级别,打印日志时从logger再到handler再到filter。而logger类也存在继承关系,始祖类是根looger是.,不同包名代表不同的层次关系。如图,com下的logger继承根logger,study和learn继承com的logger,但是两者又相互独立。

JUL日志框架的基本使用和运行流程_第1张图片

有了以上知识,可以回头看配置文件,以空行作为分隔,第一部分是根logger的配置,配置handler是consoleHandler,和对应的日志级别,第二部分是filehandler的配置,现在没有被使用,第三部分是consoleHandler的配置,配置了日志级别和打印字符串模板类。consoleHandler是控制台打印的,因此我们看到的日志是在控制台上的,又由于.和consolerhandler的日志级别都是info,而使用的logger默认会继承. ,因此控制台只会打印info之上的信息。

logger的创建

logger是由LogManager实例调用不同的方法实现,源码如:

JUL日志框架的基本使用和运行流程_第2张图片

manager又是静态创建的,从这个方法的源码,我们可以看出是懒加载的单例

JUL日志框架的基本使用和运行流程_第3张图片

manager可以确保同一个类对应的logger只会被创建一次,使得logger也是单例,可以通过Logger.getLogger获取两次logger比较两者哈希值判断是否为同一个。

从上述知识明白manager负责管理logger,我们可以进入manager的初始化方法,观察到manager调用了

image-20220407123733504

很明显这是一个读取配置的方法,其中又调用了readConfiguration(),该方法中如下部分读取了logging.properties,此部分完成操作是找到配置文件并且获取一个该文件的输入流,调用readConfigurataion读取流中的数据

JUL日志框架的基本使用和运行流程_第4张图片

读取流中的数据后,再通过反射创建所需要的配置类

JUL日志框架的基本使用和运行流程_第5张图片

自定义配置文件

知悉上述过程,我们便可以通过配置类自定义我们的根logger和其他logger,创建一份我们需要的配置文件,使用输入流读取该数据,再调用LogManager的readConfigurataion方法读取,再调用静态方法Logger.getLogger方法创建logger。

   @Test
    public void testConfig() throws IOException {
        LogManager manager = LogManager.getLogManager();
        InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("logging.properties");
        manager.readConfiguration(in);

        Logger logger = Logger.getLogger(TestJUL.class.getName());
        logger.info("info");
        logger.fine("fine");
        logger.finer("finer");
    }
/*
四月 07, 2022 12:49:46 下午 learn.log.TestJUL testConfig
信息: info
四月 07, 2022 12:49:46 下午 learn.log.TestJUL testConfig
详细: fine
*/

JUL日志框架的基本使用和运行流程_第6张图片

通过自定义方式创建自己所需要的logger

除去自定义配置文件我们也可以直接不使用默认流程而去使用,比如第一个例子,默认流程如下

 @Test
    public void testLogger() {
        Logger logger = Logger.getLogger("TestJUL.class.getName()");

        //注意,不设置这个值的化,默认会继承根logger,默认配置中就又一个consoleHandler,
        // 加下下面那个就有两个所以会打印日志两次
        logger.setUseParentHandlers(false);

        ConsoleHandler consoleHandler = new ConsoleHandler();
        SimpleFormatter simpleFormatter = new SimpleFormatter();

        consoleHandler.setFormatter(simpleFormatter);
        consoleHandler.setLevel(Level.INFO);

        logger.setLevel(Level.INFO);
        logger.addHandler(consoleHandler);

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

同样的我们可以像配置文件修改对应的值,效果也一样。这里不再赘述。

此外,文本的格式话涉及一些format的转换符,这里也不赘述。需要使用时上网查询即可。

参考资料:https://www.bilibili.com/video/BV1434y1o73n

你可能感兴趣的:(日志,java)