java日志篇(2)-JUL(java.util.logging)

 慢慢来比较快,虚心学技术

前言:之所以未按照前一篇文章 java日志篇(1)-日志概述所描述的日志发展史进行文章编写,是因为JUL是jdk自带的日志实现,其他日志实现原理大同小异,理解起来也是相对简单,如有兴趣可先去看看上一篇文章

目录

 

JUL是什么?

JUL基本使用

JUL使用分析

一、Logger

二、Handler

三、Formatter

总结

备注


JUL是什么?

JUL是jdk自带的一个日志实现,使用简单,控制灵活,如果是小型系统或者测试程序,显然JUL会是比log4j等更好的选择

JUL基本使用

使用java.util.logging.Logger的静态方法getLogger(loggerName)来获取或创建一个日志logger

import java.io.IOException;
import java.util.logging.*;

public class JULTest {
    
     public static Logger log = Logger.getLogger("TestLog");  //获取日志对象

    public static void main(String[] args) throws IOException {
        log.info("info");    //信息日志
        log.warning("warning"); //警告日志
        log.log(Level.SEVERE,"server"); //严重日志
        log.fine("fine");
    }
}

使用日志我们可以使用默认的等级实现方法进行输出:如log.info("****")等,也可以使用特定的等级进行输出:如log.log(等级,输出信息)

如上代码输出日志结果如下:

一月 14, 2019 9:20:32 下午 JULTest main
信息: info
一月 14, 2019 9:20:32 下午 JULTest main
警告: warning
一月 14, 2019 9:20:32 下午 JULTest main
严重: server

JUL使用分析

一、Logger

为什么log.fine("fine");没有输出?

我们首先看一下第一个问题,需要先了解一下JUL的日志等级划分:

JUL日志等级划分(优先级递减)及内置代表的整数如下:

  • OFF(Integer.MAX_VALUE)
  • SEVERE(1000)
  • WARNING(900)
  • INFO(800)
  • CONFIG(700)
  • FINE(500)
  • FINER(400)
  • FINEST(300)
  • ALL(Integer.MIN_VALUE)

 为了更好更灵活的控制日志的输出,jul使用如上的9个等级作为日志等级划分,其中常用的日志等级主要是server,warning和info,当为 Logger 指定了一个 Level, 该 Logger 会包含当前指定级别以及更高级别的日志,logger默认的级别是INFO,比INFO更低的日志将不显示。由上述输出结果不难看出,只是输出了info及以上等级的日志,而自动忽略info等级以下的日志内容,为什么会有这样的实现呢?此处涉及到JUL的默认配置文件loging.properties,该配置文件位于jdk安装目录的lib包下

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

com.xyz.foo.level = SEVERE

logger.log(Level.FINE,"file");调用方法如下 

public void log(Levellevel, String msg) {
          if (level.intValue() < levelValue ||levelValue == offValue) {
              return;
          }
          LogRecord lr = new LogRecord(level, msg);
          doLog(lr);
}

其中,我们可以看到.level属性指定了logger默认的日志级别为info,而logger类会先读取默认的日志级别,当选定的日志level整数值低于默认值,就不会进行日志输出

所以我们如果要更改logger的输出等级,可以通过修改对应配置项的level等级,也可以通过代码的方式去动态设置logger的等级

如下:结果输出为所有日志信息

import java.io.IOException;
import java.util.logging.*;

public class JULTest {
    
     public static Logger log = Logger.getLogger("TestLog");  //获取日志对象

    public static void main(String[] args) throws IOException {
        
        log.setLevel(Level.ALL);//设置logger的日志级别为全部,默认输出所有级别日志信息

        log.info("info");    //信息日志
        log.warning("warning"); //警告日志
        log.log(Level.SEVERE,"server"); //严重日志
        log.fine("fine");
    }
}


输出结果:
一月 15, 2019 12:12:23 上午 JULTest main
信息: info
一月 15, 2019 12:12:23 上午 JULTest main
警告: warning
一月 15, 2019 12:12:23 上午 JULTest main
严重: server
一月 15, 2019 12:12:23 上午 JULTest main
较详细: fine

二、Handler

既已引出logging.properties文件,我们就接着以此作为分析入口

由文件内容可以看到上一篇文章中介绍的handler组件的配置

默认Handler:   

JUL中用的比较多的是两个Handler类:ConsoleHandler和FileHandler

其中,ConsoleHandler是对控制台输出的默认处理类,FileHandler是对文件输出的默认处理类

 虽然我们可以通过修改配置文件更改系统日志的处理方式,但是更灵活的方式是从代码进行更改

举例:1、更改控制台Handler处理类的默认级别

import java.io.IOException;
import java.util.logging.*;

public class JULTest {
    
     public static Logger log = Logger.getLogger("TestLog");  //获取日志对象

    public static void main(String[] args) throws IOException {
        
        log.setLevel(Level.ALL);//设置logger的日志级别为全部,默认输出所有级别日志信息

        log.setUseParentHandlers(false); //禁用日志原本处理类

        ConsoleHandler consoleHandler = new ConsoleHandler(); //创建控制台输出控制Handler
        consoleHandler.setLevel(Level.INFO); //设置控制台输出级别
        log.addHandler(consoleHandler); //将Handler加入logger中

        log.info("info");    //信息日志
        log.warning("warning"); //警告日志
        log.log(Level.SEVERE,"server"); //严重日志
        log.fine("fine");
    }
}


输出结果:
一月 15, 2019 12:03:56 上午 JULTest main
信息: info
一月 15, 2019 12:03:56 上午 JULTest main
警告: warning
一月 15, 2019 12:03:56 上午 JULTest main
严重: server

上面的代码新建了一个控制台Handler,级别设置为INFO,所以结果又变成了只输出INFO及以上级别的日志

注:如果没有log.setUseParentHandlers(false); 父Handler与子Handler都会生效,此时会输出两遍日志内容

2、更改文件FileHandler处理类的处理方式

import java.io.IOException;
import java.util.logging.*;

public class JULTest {
    
     public static Logger log = Logger.getLogger("TestLog");  //获取日志对象

    public static void main(String[] args) throws IOException {
        
        log.setLevel(Level.ALL);//设置logger的日志级别为全部,默认输出所有级别日志信息

        log.setUseParentHandlers(false); //禁用日志原本处理类

        FileHandler fileHandler = new FileHandler("日志路径/testJUL.log");
        fileHandler.setLevel(Level.ALL); //记录级别
        log.addHandler(fileHandler); //添加Handler

        log.info("info");    //信息日志
        log.warning("warning"); //警告日志
        log.log(Level.SEVERE,"server"); //严重日志
        log.fine("fine");
    }
}

 testJUL.log的内容如下





  2019-01-15T00:22:58
  1547482978598
  0
  TestLog
  INFO
  JULTest
  main
  1
  info


  2019-01-15T00:22:58
  1547482978622
  1
  TestLog
  WARNING
  JULTest
  main
  1
  warning


  2019-01-15T00:22:58
  1547482978622
  2
  TestLog
  SEVERE
  JULTest
  main
  1
  server


  2019-01-15T00:22:58
  1547482978622
  3
  TestLog
  FINER
  JULTest
  main
  1
  fine


3、自定义Handler

有时候我们在日志中做些自定义的操作,此时我们需要编写自定义Handler并加入logger的Handlers中

//继承Handler类编写自定义Handler
public class MyHandler extends Handler {

    private LogRecord record;
    
    //
    @Override
    public void publish(LogRecord record) {
        this.record = record;
        ........(自定义操作)
    }

    @Override
    public void flush() {
        System.out.println("logger:"+this.record.getLoggerName()+"flush");
    }

    @Override
    public void close() throws SecurityException {
        System.out.println("logger:"+this.record.getLoggerName()+"close");
    }
}

public class JULTest {
    
     public static Logger log = Logger.getLogger("TestLog");  //获取日志对象

    public static void main(String[] args) throws IOException {
        
        log.setLevel(Level.ALL);//设置logger的日志级别为全部,默认输出所有级别日志信息

        log.setUseParentHandlers(false); //禁用日志原本处理类

        MyHandler myHandler = new MyHandler();  //创建自定义日志处理类实体
        log.addHandler(myHandler); //添加日志处理实体类

        log.info("info");    //信息日志
        log.warning("warning"); //警告日志
        log.log(Level.SEVERE,"server"); //严重日志
        log.fine("fine");
    }
}

 

三、Formatter

可以看到上述我们的日志文件中默认输出是xml格式,很明显是不利于查看的。此时我们需要去更改日志输出样式,同样的可以使用代码实现:

//继承Formatter自定义日志输出形式
public class MyFormate extends Formatter {

    @Override
    public String format(LogRecord record) {
        return new Date()+"-["+record.getSourceClassName()+"."+record.getSourceMethodName()+"]"+record.getLevel()+":"+record.getMessage()+"\n";
    }
}



public class JULTest {
    
     public static Logger log = Logger.getLogger("TestLog");  //获取日志对象

    public static void main(String[] args) throws IOException {
        
        log.setLevel(Level.ALL);//设置logger的日志级别为全部,默认输出所有级别日志信息

        log.setUseParentHandlers(false); //禁用日志原本处理类

        FileHandler fileHandler = new FileHandler("日志路径/testJUL.log");
        fileHandler.setLevel(Level.ALL); //记录级别

       fileHandler.setFormatter(new MyFormate()); //设置自定义样式

        log.addHandler(fileHandler); //添加Handler

        log.info("info");    //信息日志
        log.warning("warning"); //警告日志
        log.log(Level.SEVERE,"server"); //严重日志
        log.fine("fine");
    }
}

此时testJUL.log的输出样式更直观简洁了:

Tue Jan 15 00:36:09 CST 2019-[JULTest.main]INFO:info
Tue Jan 15 00:36:09 CST 2019-[JULTest.main]WARNING:warning
Tue Jan 15 00:36:09 CST 2019-[JULTest.main]SEVERE:server
Tue Jan 15 00:36:09 CST 2019-[JULTest.main]FINER:fine

总结

       1、JUL通过对级别的整数值比对决定是否输出日志,对外则是使用通俗易懂的Level名,这样的设计使得系统设计清晰简便,值得学习。

       2、JUL的logging.propertise配置了基本的日志控制属性,可以通过更改该配置文件控制系统日志的输出

       3、JUL的Handler使得JUL的日志控制十分的灵活,但是注意logger可以具备多个Handler,需要考虑Handler之间的干扰和冗余可能

      4、JUL的Formatter使JUL的日志输出更加优雅美观,在学习的过程中,我们可以更加多的考虑如何精准输出日志所需要的信息,比如类名,方法名等

备注

多参考前辈,源码未深入理解,还望多多探讨,文章结构不大严谨,后续继续完善,谢谢

部分内容参考文章:qingkangxu-JDK Logging深入分析

你可能感兴趣的:(java)