计算机 · 2021年12月18日 0

Java Logging

Java自带的log工具。

入门

打印信息:

import java.util.logging.Logger;

public class MyClass {
  private static final Logger LOGGER = Logger.getLogger(MyClass.class.getName());

  public static void main(String[] args) {
      System.out.println("main method starts");
      LOGGER.info("in MyClass");
  }
}

JUL的workflow

  • Logger
    A named entity to which user application sends messages.
  • Handler
    A specific handler is typically tied to a specific logging destination. A destination is where final formatted messages are sent. A handler can apply any additional logic, e.g. to improve performance.
    Examples: ConsoleHandler, FileHandler, SocketHandler, user defined ones.
  • Level
    Used by Logger/Handler. To discard messages if the Level of a given message is less than the Level associated with the Logger/Handler.
  • Filter
    Used by Logger/Handler. It can filter message which cannot be filtered based on Levels. A dynamic/programmatic way to filter messages.
  • Formatter
    To display messages in a specific format.
    Examples: SimpleFormatter, XMLFormatter or user defined ones
  • Resource Bundle
    A Logger may have a ResourceBundle associated with it. It is used to map raw message strings with localized message strings.

Logger的层次结构:
root Logger(“”)
Logger1 > “com.logicbig”
Logger2 > “com.logicbig.MyClass2” > parent namespace= “com.logicbig”
Logger3 > “com.logicbig.MyClass3” > parent namespace= “com.logicbig”

推荐使用带有包名信息的类名作为Logger的名字。

配置日志的属性:

  1. 用JVM属性指定Config class:java.util.logging.config.class=com.logicbig.example.MyConfigClass
    Java会在启动时载入MyConfigClass。此类的构造函数可以使用下面的方法初始化日志属性:
LogManager.getLogManager().readConfiguration(InputStream);

在这个用于初始化日志属性的类里,也可以设置各种logger:

Logger mainLogger = Logger.getLogger("com.logicbig.example");
mainLogger.setLevel(Level.FINEST);
mainLogger.addHandler(....);
 ....        
  1. 用JVM属性指定配置文件:java.util.logging.config.file=D:\myApp\logging.properties
  2. 默认的日志属性文件(C:\java\jdk1.8.0_111\jre\lib\logging.properties):
############################################################
#  	Default Logging Configuration File
#
# You can use a different file by specifying a filename
# with the java.util.logging.config.file system property.
# For example java -Djava.util.logging.config.file=myfile
############################################################

############################################################
#  	Global properties
############################################################

# "handlers" specifies a comma separated list of log Handler
# classes.  These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler

# To also add the FileHandler, use the following line instead.
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers.  For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

# default file output is in user's home directory.
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

# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

# Example to customize the SimpleFormatter output format
# to print one-line log message like this:
#     <level>: <log message> [<date/time>]
#
# java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n

############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################

# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
com.xyz.foo.level = SEVERE

在程序中配置日志属性的几种方法

按照绝对路径读取配置文件

public class MyClass {

  private static Logger LOGGER;

  static {
      System.setProperty("java.util.logging.config.file",
              "d:\\test-app\\logging.properties");
      //must initialize loggers after setting above property
      LOGGER = Logger.getLogger(MyClass.class.getName());
  }

  public static void main(String[] args) {
      System.out.println("-- main method starts --");
      LOGGER.info("an info msg");
      LOGGER.warning("a warning msg");
      LOGGER.severe("a severe msg");
  }
}

按照classpath读取配置文件

public class MyClass2 {

  private static Logger LOGGER;

  static {
      String path = MyClass2.class.getClassLoader()
                                  .getResource("logging.properties")
                                  .getFile();
      System.setProperty("java.util.logging.config.file", path);
      LOGGER = Logger.getLogger(MyClass2.class.getName());
  }

  public static void main(String[] args) {
      System.out.println("-- main method starts --");
      LOGGER.info("an info msg");
      LOGGER.warning("a warning msg");
      LOGGER.severe("a severe msg");
  }
}

使用LogManager#readConfiguration()读取配置文件

public class MyClass3 {
  private static Logger LOGGER;

  static {
      InputStream stream = MyClass3.class.getClassLoader().
              getResourceAsStream("logging.properties");
      try {
          LogManager.getLogManager().readConfiguration(stream);
      } catch (IOException e) {
          e.printStackTrace();
      }
      LOGGER = Logger.getLogger(MyClass3.class.getName());
  }

  public static void main(String[] args) {
      System.out.println("-- main method starts --");
      LOGGER.info("an info msg");
      LOGGER.warning("a warning msg");
      LOGGER.severe("a severe msg");
  }
}

设置日志格式

在使用SimpleFormatter设置日志输出格式时,默认调用以下方法输出日志:
String.format(format, date, source, logger, level, message, thrown);

定义自己的输出格式有以下几种方法:

使用logging.properties
src/main/resources/logging.propertis:

  handlers= java.util.logging.ConsoleHandler
  .level= INFO
  java.util.logging.ConsoleHandler.level = INFO
  java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
  java.util.logging.SimpleFormatter.format=[%1$tF %1$tT] [%4$-7s] %5$s %n
public class MyClass {
  private static Logger LOGGER = null;

  static {
      InputStream stream = MyClass.class.getClassLoader().
              getResourceAsStream("logging.properties");
      try {
          LogManager.getLogManager().readConfiguration(stream);
          LOGGER= Logger.getLogger(MyClass.class.getName());

      } catch (IOException e) {
          e.printStackTrace();
      }
  }

  public static void main(String[] args) {
      System.out.println("-- main method starts --");
      LOGGER.info("in MyClass");
      LOGGER.warning("a test warning");
  }
}

使用system property

public class MyClass2 {
  private static Logger LOGGER = null;

  static {
      System.setProperty("java.util.logging.SimpleFormatter.format",
              "[%1$tF %1$tT] [%4$-7s] %5$s %n");
      LOGGER = Logger.getLogger(MyClass2.class.getName());
  }

  public static void main(String[] args) {
      System.out.println("-- main method starts --");
      LOGGER.info("in MyClass2");
      LOGGER.warning("a test warning");
  }
}

通过编程指定

package com.logicbig.example;

import java.util.Date;
import java.util.logging.ConsoleHandler;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

public class MyClass3 {
  private static Logger LOGGER = null;

  static {
      Logger mainLogger = Logger.getLogger("com.logicbig");
      mainLogger.setUseParentHandlers(false);
      ConsoleHandler handler = new ConsoleHandler();
      handler.setFormatter(new SimpleFormatter() {
          private static final String format = "[%1$tF %1$tT] [%2$-7s] %3$s %n";

          @Override
          public synchronized String format(LogRecord lr) {
              return String.format(format,
                      new Date(lr.getMillis()),
                      lr.getLevel().getLocalizedName(),
                      lr.getMessage()
              );
          }
      });
      mainLogger.addHandler(handler);
      LOGGER = Logger.getLogger(MyClass3.class.getName());
  }

  public static void main(String[] args) {
      System.out.println("-- main method starts --");
      LOGGER.info("in MyClass3");
      LOGGER.warning("a test warning");
  }
}

设置日志级别

api:Logger#setLevel()和Handler#setLevel()

在程序中设置日志级别

public class LogLevelExample {
  private static Logger log = Logger.getLogger(LogLevelExample.class.getName());

  static {
      System.setProperty("java.util.logging.SimpleFormatter.format",
              "[%1$tF %1$tT %1$tL] [%4$-7s] %5$s %n");
  }

  public static void main(String[] args) throws Exception {
      setLevel(Level.ALL);
      Set<Level> levels = getAllLevels();
      int i = 1;
      for (Level level : levels) {
          log.log(level, level.getName() + " - " + (i++));
      }
  }

  public static void setLevel(Level targetLevel) {
      Logger root = Logger.getLogger("");
      root.setLevel(targetLevel);
      for (Handler handler : root.getHandlers()) {
          handler.setLevel(targetLevel);
      }
      System.out.println("level set: " + targetLevel.getName());
  }

  public static Set<Level> getAllLevels() throws IllegalAccessException {
      Class<Level> levelClass = Level.class;

      Set<Level> allLevels = new TreeSet<>(
              Comparator.comparingInt(Level::intValue));

      for (Field field : levelClass.getDeclaredFields()) {
          if (field.getType() == Level.class) {
              allLevels.add((Level) field.get(null));
          }
      }
      return allLevels;
  }
}

在配置文件中设置日志级别

src/main/resources/logging.properties

handlers= java.util.logging.ConsoleHandler
.level= FINEST
java.util.logging.ConsoleHandler.level = FINEST
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=[%1$tF %1$tT] [%4$-7s] %5$s %n
public class LogLevelPropertiesExample {
  private static Logger log;

  static {
      String path = LogLevelPropertiesExample.class
              .getClassLoader()
              .getResource("logging.properties")
              .getFile();
      System.setProperty("java.util.logging.config.file", path);
      log = Logger.getLogger(LogLevelPropertiesExample.class.getName());
  }

  public static void main(String[] args) throws Exception {
      Set<Level> levels = getAllLevels();
      int i = 1;
      for (Level level : levels) {
          log.log(level, level.getName() + " - " + (i++));
      }
  }
    .............
}

Java Util Logging,自定义如何处理log信息

通过自定义一个Log Handler,可以由开发者自行决定如何处理来自logger的日志消息。

自定义handler

package com.logicbig.example;

import java.util.logging.Handler;
import java.util.logging.LogRecord;

public class MyLogHandler extends Handler {

  @Override
  public void publish(LogRecord record) {
      StringBuilder sb = new StringBuilder();
      sb.append(record.getMillis())
        .append(" - ")
        .append(record.getSourceClassName())
        .append("#")
        .append(record.getSourceMethodName())
        .append(" - ")
        .append(record.getMessage());
      System.out.println(sb.toString());
  }

  @Override
  public void flush() {
  }

  @Override
  public void close() throws SecurityException {
  }
}

生成日志消息

package com.logicbig.example;

import java.util.logging.Logger;

public class AppClass {
  private static final Logger LOGGER = Logger.getLogger(AppClass.class.getName());

  public void doSomething(){
      LOGGER.info("in AppClass");
  }
}

注册handler

package com.logicbig.example;

import java.util.logging.Handler;
import java.util.logging.LogManager;
import java.util.logging.Logger;

public class ExampleMain {

  public static void main(String[] args) {
      //reset() will remove all default handlers
      LogManager.getLogManager().reset();
      Logger rootLogger = LogManager.getLogManager().getLogger("");

      rootLogger.addHandler(new MyLogHandler());
      AppClass appClass = new AppClass();
      appClass.doSomething();
      rootLogger.info("some message");
  }
}