《Java 核心技术卷1 第10版》学习笔记------日志

日志 API 的优点: 

  1.  可以很容易地取消全部日志记录, 或者仅仅取消某个级别的日志, 而且打开和关闭这个操作也很容易 。
  2.  可以很简单地禁止日志记录的输出, 因此, 将这些日志代码留在程序中的开销很小 。
  3.  日志记录可以被定向到不同的处理器, 用于在控制台中显示, 用于存储在文件中等 。
  4.  日志记录器和处理器都可以对记录进行过滤 。 过滤器可以根据过滤实现器制定的标准丢弃那些无用的记录项 。
  5.  日志记录可以采用不同的方式格式化, 例如, 纯文本或 XML 。 
  6.  应用程序可以使用多个日志记录器 , 它们使用类似包名的这种具有层次结构的名字,例如 , com.mycompany .myapp。
  7.  在默认情况下 , 日志系统的配置由配置文件控制 。 如果需要的话, 应用程序可以替换这个配置

基本日志:

要生成简单的日志记录, 可以使用全局日志记录器 (global logger ) 并调用其 info 方法 :
 

    public static void main(String[] args) {

        Logger globalLogger =  Logger.getGlobal(); // 获得全局日志记录器
        globalLogger.setLevel(Level.OFF);   // 关闭日志

        // 记录日志
        globalLogger.info("File-Open menu item selected!");
        // 二月 17, 2019 5:59:09 下午 com.chapter7.LogLearnMain main
        // 信息: File-Open menu item selected!

        globalLogger.setLevel(Level.ALL);   // 开启记录所有类型的记录
        globalLogger.warning("user no login!");

        // 最终输出以下内容:
        // 二月 17, 2019 6:07:55 下午 com.chapter7.LogLearnMain main    [时间、 所在的类名及方法名]
        // 警告: user no login!   [日志级别、日志信息]
    }

高级日志

在一个专业的应用程序中,通常是不会将所有的日志都记录到一个全局日志记录器中,而是可以自定义日志记录器

1. 创建或获取日志记录器

private static final Logger myLogger = Logger.getLogger("com.mycompany.myapp");
/* 未被任何变量引用的日志记录器可能会被垃圾回收。为了防止这中情况的发生,
要像上面的例子中一样,用一个静态变量存储日志记录器的一个引用。
*/

与包名类似,日志记录器也具有层次结构。事实上,与包名相比,日志记录器的层次结构性更强。 对于包来说 , 一个包的名字与其父包的名字之间没有语义关系, 但是日志记录器的父与子之间将共享某些属性 。例如 , 如果对 com.mycompany 日志记录器设置了日志级别 ,它的子记录器也会继承这个级别 ,

通常日志级别有以下七个:【从左到右级别依次降低,详细程度依次增加】

SEVERE、WARING、INFO、CONFIG、FINE、FINER、FINEST

默认情况下只记录前三个级别【即:SEVERE、WARING、INFO】。

2. 更改日志记录级别:

// 更改日志记录级别
logger.setLevel(Level.FINE); // 现在可以记录【SEVERE、WARING、INFO、CONFIG、FIINE】
// 关闭日志记录
logger.setLevel(Level.OFF);
// 开启所有级别
logger.setLevel(Level.ALL);

3. 记录日志

// way 1 
logger.waring(message);    // 记录 WARING 级别的日志
logger.fine(message);    // 记录 FINE 级别的日志
...
// way 2
logger.log(Level.FINE, message); // 记录 FINE 级别的日志

默认的日志记录将显示包含时间、日志调用的类名方法名 , 如同堆栈所显示的那样 。但是 ,如果虚拟机对执行过程进行了优化, 就得不到准确的调用信息 。 此时 , 可以调用 logp 方法获得调用类和方法的确切位置 , 这个方法的签名为 :

// Logger 中的 logp 方法
void logp(Level l, String className, String methodName, String message);

// 下面有一些用来跟踪执行流的方法
void entering(String className, String methodName);
void entering(String className, String methodName, Object param);
void entering(String className, String methodName, Object[] params);

void exiting(String className, String methodName);
void exiting(String className, String methodName, Object result);

// 执行流 Demo
public class LogMain {
    private static final Logger myLogger = Logger.getLogger("com.chapter7.LogLearnMain");

    public static void main(String[] args) {
        // 测试执行流
        myLogger.setLevel(Level.FINER);
        sayWord("你好!");
    }

    public static int sayWord(String word){
        int result = 0;
        // 跟踪执行流-进入
        myLogger.entering("com.chapter7.LogLearnMain", "sayWord", word);
        // 使用 logp 获得调用类和方法的确切位置
        myLogger.logp(Level.INFO, "com.chapter7.LogLearnMain", "sayWord", "logp");

        if (!word.equals(null) && word.length()>0)
            System.out.println("Say: " + word);
        else
            result=-1;
        // // 跟踪执行流-退出
        myLogger.exiting("com.chapter7.LogLearnMain", "sayWord", result);
        return result;
    }
}
// 这些调用将生成 FINER 级别和以字符串 ENTRY 和 RETURN 开始的日志记录 。
// PS: 默认情况下我们是看不到以 ENTRY RETURN 开始的日志记录的,具体原因往下看【日志处理器部分】,
// 这里只给出解决办法,修改 jre/lib/logging.properties 中的 
// java.util.logging.ConsoleHandler.level = FINEST,当然由于日志级别的是 FINER 级别
// 的(不在默认的级别内),所以还要重设 logger.setLevel(Level.FINER) 才行。

demo运行截图:

《Java 核心技术卷1 第10版》学习笔记------日志_第1张图片

记录日志的常见用途是记录那些不可预料的异常 。可以使用下面两个方法提供日志记录中包含的异常描述内容 

// 记录异常
void throwing(String className, String methodName, Throwable t);
void log(Level l, String message, Throwable t);

// 典型的用法 way 1
if(...){
    IOException exception = new Exception(" exception info ");
    logger.throwing("com.mycompany.mylib.Reader", "read", exception);
    throw exception;
}

// 典型的用法 way 2
try{
    ...
}catch(IOException e){
    Logger.getLogger("").log(Level.WARING, "Reading image", e);
}

//PS: 调用 throwing 可以记录一条 FINER 级别的记录和一条以 THROW 开始的信息

修改日志管理器配置

可以通过编辑配置文件来修改日志系统的各种属性。默认情况下配置文件在:jre/lib/logging.properties

要想使用另一个配置文件,就要将 java.util.logging.config.file 属性字段的值设置为自定义配置文件存储位置,并用下列命令启动应用程序:

java -D java.util.logging.config.file=configFile MainClass

# 日志管理器【LogManager】在 JVM 启动过程中初始化,即在 main 函数执行前完成。
# 如果在 main 中调用 System.setProperty("java.util.logging.config.file", file), 
# 也会调用 LogManager.readConfiguration() 来重新初始化日志管理器

要想修改默认的日志记录器级别,就需要编辑配置文件,并修改以下属性字段

.level=INFO

可以通过添加以下内容来指定自己的日志记录级别

com.mycompany.myapp.level=FINE

即在日志记录器后面添加后缀 .level 即可。

在稍后可以看到 , 日志记录并不将消息发送到控制台上 , 这是处理器的任务 。 另外 , 处理器也有级别 。 要想在控制台上看到 FINE 级别的消息, 就需要进行下列设置。
java.util.logging.ConsoleHandler.level = FINE

本地化

主要用于国际化程序处理,暂略。key word: resource bundle

处理器

默认情况下日志记录器将记录发送到 ConsoleHandler 中, 并由它输出到 System . err 流中 。特别是 , 日志记录器还会将记录发送到父处理器中 , 而最终的处理器(命名为 “ ” )有一个 ConsoleHandler 。

与日志记录器一样 , 处理器也有日志记录级别 对于一个要被记录的日志记录, 它的日志记录级别必须高于日志记录器和处理器的阈值 。 日志管理器配置文件设置的默认控制台处理器的日志记录级别为 INFO。

要想记录 FINE 级别的日志, 就必须修改配置文件中的默认日志记录级别和处理器级别(上面Demo中的操作) 。另外 , 还可以绕过配置文件 , 安装自己的处理器 。

// 使用自定义处理器
Logger logger = Logger.getLogger("com.mycompany.myapp");
logger.setLevel(Level.FINE);
logger.setUseParentHandlers(flase);    // 不使用父处理器
// 自定义处理器
Handler handler = new ConsoleHandler();
handler.setLevel(Level.FINE);   // 设置处理器级别
logger.addHandler(handler);    // 使用自定义处理器

在默认情况下, 日志记录器将记录发送到自己的处理器和父处理器 。 我们的日志记录器是原始日志记录器 ( 命名为 “ ” )的子类 , 而原始日志记录器将会把所有等于或高于 INFO级別的记录发送到控制台 。 然而 , 我们并不想两次看到这些记录 。 鉴于这个原因 , 应该将useParentHandlers  设置为 false 。

要想将日志记录发送到其他地方, 就要添加其他的处理器 。 日志 API 为此提供了两个很有用的处理器 , 一个是 FileHandler ; 另 一个是 SocketHandler 。 SocketHandler 将记录发送到特定的主机和端口 :  而更令人感兴趣的是 FileHandler , 它可以收集文件中的记录 。

// 使用 FileHandler 
FileHandler handler = new FileHandler();
logger.addHandler(handler);

这些记录被发送到用户主目录的 javan.log 文件中 , n 是文件名的唯一编号 。 如果用户系统没有主目录 ( 例如 , 在 Windows 95/98/ Me,) 文件就存储在 C:\ Window 这样的默认位置上。
在默认情况下 , 记录被格式化为 XML 。 下面是一个典型的日志记录的形式 :


    2002-02 -04T 07:45: 15
    1012837515710
    1
    <1ogger>com.mycompany.myapp
    INFO
     com.mycompany.mylib.Reader
    read
    10
    Reading file corejava.gif

可以通过设置 H 志管理器配置文件中的不同参数 (请参看表 7 - 1 , ) 或者利用其他的构造器 ( 请参看本节后面给出的 APf 注释) 来修改文件处理器的默认行为 。
《Java 核心技术卷1 第10版》学习笔记------日志_第2张图片

也有可能不想使用默认的日志记录文件名 , 因此, 应该使用另一种模式 , 例如, %h/myapp.log。

《Java 核心技术卷1 第10版》学习笔记------日志_第3张图片

如果多个应用程序 ( 或者同一个应用程序的多个副本 ) 使用同一个口志文件 就应该开,启 append 标志 。 另外 , 应该在文件名模式中使用 %u , 以便每个应用程序创建日志的唯一副本 。

开启文件循环功能也是一个不错的主意 。 日志文件以 myapp .log .O , myapp.log. 1 , myapp.log. 2 , 这种循环序列的形式出现 3 只要文件超出了大小限制 , 最旧的文件就会被删除 , 其他的文件将重新命名 , 同时创建一个新文件 , 其编号为 0 。

自定义处理器

通过扩展 handler 或者是 StreamHandler 类自定义处理器。暂略

过滤器

在默认情况下 , 过滤器根据日志记录的级别进行过滤 。 每个日志记录器和处理器都可以有一个可选的过滤器来完成附加的过滤 。另外, 可以通过实现 Filter 接口并定义下列方法来自定义过滤器 。

boolean isLoggable(LogRecord record);

在这个方法中, 可以利用自己喜欢的标准 , 对日志记录进行分析 , 返回 true 表示这些录应该包含在日志中 。 例如, 某个过滤器可能只对 entering 方法和 exiting 方法产生的消息感兴趣 , 这个过滤器可以调用 record. getMessage() 方法 , 并査看这个消息是否用 ENTRY 或RETURN 开头 。

要想将一个过滤器安装到一个日志记录器或处理器中 , 只需要调用 setFilter 方法就可以了 。 注意, 同一时刻最多只能有一个过滤器 。

格式化器

ConsoleHandler 类和 FileHandler 类可以生成文本和 XML 格式的日志记录 。 但是, 也可以自定义格式 。 这需要扩展 Formatter 类并覆盖下面这个方法 

String format(LogRecord record )

可以根据自己的愿望对记录中的信息进行格式化, 并返冋结果字符串。 在 format 方法中, 有可能会调用下面这个方法

String formatMessage(LogRecord record)

这个方法对记录中的部分消息进行格式化、 参数替换和本地化应用操作 。
很多文件格式 ( 如 XML ) 需要在已格式化的记录的前后加上一个头部和尾部在这个例子中, 要覆盖下面两个方法 :

String getHead(Handler h )
String getTail(Handler h)

最后, 调用 setFormatter 方法将格式化器安装到处理器中 。

日志记录说明

面对日志记录如此之多的可选项 , 很容易让人忘记最基本的东西。下面的 “日志说明书 ”总结了一些最常用的操作。

1. 为一个简单的应用程序, 选择一个日志记录器 , 并把日志记录器命名为与主应用程序包一样的名字 , 例如, com . mycompany . myprog , 这是一种好的编程习惯 。 另外 , 可以通过调用下列方法得到日志记录器 。

2. 默认的日志配置将级别等于或高于 INFO 级别的所有消息记录到控制台 。 用户可以覆盖默认的配置文件 。 但是正如前面所述 , 改变配置需要做相当多的工作 。 因此, 最好在应用程序中安装一个更加适宜的默认配置 。

下列代码确保将所有的消息记录到应用程序特定的文件中 。 可以将这段代码放置在应用程序的 main 方法中 。

        if(System.getProperty("java.util.logging.config.class") == null
                && System.getProperty("java.util.logging.config.file") == null){

            try {
                Logger.getLogger("").setLevel(Level.ALL);
                final int LOG_ROTATION_COUNT = 10;
                Handler handler = null;
                handler = new FileHandler("%h/myapp.log", 0, LOG_ROTATION_COUNT);
                Logger.getLogger("").addHandler(handler);
            } catch (IOException e) {
                e.printStackTrace();
                Logger.getLogger("").log(Level.SEVERE, "Cant't create log file handler", e);
            }
        }

3. 现在, 可以记录自己想要的内容了 。 但需要牢记 : 所有级别为 INFO 、 WARNING 和 SEVERE 的消息都将显示到控制台上。因此 最好只将对程序用户有意义的消息设置为这几个级别 。将程序员想要的日志记录, 设定为 FINE 是一个很好的选择 。

当调用 System. out.println 时, 实际上生成了下面的日志消息 :

logger.fine( "File open dialog canceled";)

记录那些不可预料的异常也是一个不错的想法 , 例如 :

try{
    ...
}catch(SomeException e){
    logger.log(Level.FINE, "explanation", e);
}

 

你可能感兴趣的:(JAVA)