【自学】Java核心技术卷1_7.5日志

参考博客1博客2 

日志基础

在项目开发过程中,可以通过 debug 查找问题。而在线上环境查找问题只能通过打印日志的方式查找问题。Java使用了一种自定义的、可扩展的方法来输出日志。虽然Java通过java.util.logging包提供了一套基本的日志处理API,但你可以很轻松的使用一种或者多种其它日志解决方案。这些解决方案尽管使用不同的方法来创建日志数据,但它们的最终目标是一样的,即将日志从你的应用程序输出到目标地址

日志组件

Java日志API由以下三个核心组件组成:

  • Loggers:Logger负责捕捉事件并将其发送合适的Appender。
  • Appenders:也被称为Handlers,负责将日志事件记录到目标位置。在将日志事件输出之前,Appenders使用Layouts来对事件进行格式化处理。
  • Layouts:也被称为Formatters,它负责对日志事件中的数据进行转换和格式化。Layouts决定了数据在一条日志记录中的最终形式

Logger记录一个事件时,它将事件转发给适当的Appender。然后Appender使用Layout来对日志记录进行格式化,并将其发送给控制台、文件或者其它目标位置。另外,Filters可以让你进一步指定一个Appender是否可以应用在一条特定的日志记录上。在日志配置中,Filters并不是必需的,但可以让你更灵活控制日志消息的流动。

日志框架

  • 在Java中,输出日志需要使用一个或者多个日志框架,这些框架提供了必要的对象、方法和配置传输消息(将日志从你的应用程序输出到目标地址)
  • Java日志框架有
  1. java.util.logging包(默认)
  2. 第三方框架(Log4jLogbacktinylog)
  3. 抽象层开发包(SLF4JApache Commons Logging)
  • 配置日志框架:代码、外部配置文件。尽管所有的Java日志框架都可以通过代码进行配置,但大部分配置还是通过外部配置文件完成的,这些文件决定了日志消息在何时通过什么方式进行处理,日志框架可以在运行时加载这些文件。
  • 如何选择一个日志解决方案,这取决于你的日志需求的复杂度、和其它日志解决方案的兼容性、易用性、框架在基于Java的各种不同项目上的支持程度以及个人喜好。

抽象层

对代码和日志框架进行解耦,从而允许你在不同的日志框架中进行切换。诸如SLF4J这样的抽象层,会将你的应用程序从日志框架中解耦。应用程序可以在运行时选择绑定到一个特定的日志框架(例如java.util.logging、Log4j或者Logback),这通过在应用程序的类路径中添加对应的日志框架来实现。如果在类路径中配置的日志框架不可用,抽象层就会立刻取消调用日志的相应逻辑。抽象层可以让我们更加容易地改变项目现有的日志框架,或者集成那些使用了不同日志框架的项目。

《阿里巴巴Java开发手册(正式版)》 中提出:
应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。

日志级别

  • Log levels provide a way to categorize and identify logs based on their severity. 日志级别用来确定日志的严重程度,它可以用来过滤日志事件或者将其发送给不同的 Appender。
  • java.util.logging框架提供的日志级别:SEVERE (HIGHEST LEVEL)、WARNING、INFO、CONFIG、FINE、FINER、FINEST (LOWEST LEVEL)。另外, 还有两个日志级别:ALL和OFF。ALL会让Logger输出所有消息,而OFF则会关闭日志功能。
  • 设置日志级别:在设定日志级别后,Logger会自动忽略那些低于设定级别的日志消息

例如,下面的代码会让Logger记录任何WARNING或者更高级别的日志消息,忽略那些低于WARNING级别的日志消息:

1

logger.setLevel(Level.WARNING);

配置文件中设置Logger的日志级别:

1

2

3

4

...

<Loggers>

  <Logger name="MyLogger" level="warning">

  ...

Loggers

  • Loggers是用来触发日志事件的对象在Java应用程序中被创建和调用,然后Loggers才会将事件传递给Appender。
  • 一个类中可以包含针对不同事件的多个独立的Loggers,你也可以在一个Loggers里面内嵌一个Loggers,从而创建一种Loggers层次结构

1、创建新Logger

  • 在不同的日志框架下面创建新Logger过程大同小异,尽管调用的具体方法名称可能不同。
  • 在使用 java.util.logging 时,你可以通过Logger.getLogger() 方法创建新Logger,这个方法接收一个string参数,用于指定Logger的名字。如果指定名字的Logger已经存在,那么只需要返回已经存在的Logger;否则,程序会创建一个新Logger。通常情况下,一种好的做法是我们在当前类下使用 class.getName() 作为新Logger的名字
 

Logger logger = Logger.getLogger(MyClass.class.getName());

2、记录日志事件

  • Logger提供了几种方法来触发日志事件。在你记录一个事件之前,你还需要设置级别。
  • Logger.log() 方法除了日志消息以外,还需要一个日志级别作为参数:

1

logger.log(Level.WARNING, “This is a warning!”);

  • 大部分日志框架都针对输出特定级别日志提供了快捷方式。例如,下面语句的作用和上面语句的作用是一样的:

1

logger.warning(“This is a warning!”);

  • 还可以阻止Logger输出低于指定日志级别的消息下面的示例中,Logger只能输出高于WARNING级别的日志消息,并丢弃日志级别低于WARNING的消息:

1

logger.setLevel(Level.WARNING);

  • PS:还有另外一些方法可以用来记录额外的信息。logp()(精确日志)可以让你指定每条日志记录的源类(source class)和方法,而 logrb()(使用资源绑定的日志)可以让你指定用于提取日志消息的资源。entering() 和 exiting() 方法可以让你记录方法调用信息,从而追踪程序的执行过程。

Appenders

  • Appenders将日志消息转发给期望的输出它负责接收日志事件,使用Layout格式化事件,然后将其发送给对应的目标。对于一个日志事件,我们可以使用多个Appenders来将事件发送到不同的目标位置。例如,我们可以在控制台上显示一个简单的日志事件的同时,将其通过邮件的方式发送给指定的接收者。
  • 请注意:在java.util.logging中,Appenders被称作Handlers。

1、增加Appender(代码、配置文件)

  • 大部分日志框架的Appender都会执行类似的功能,但在实现方面大相径庭。如果使用 java.util.logging,可以用 Logger.addHandler() 方法将Appender添加到Logger中。例如,下面的代码添加了一个新的ConsoleHandler,它会将日志输出到控制台:

1

logger.addHandler(new ConsoleHandler());

  • 一种更常用的添加Appender的方式是使用配置文件

1如果使用 java.util.logging,Appenders会定义一个以逗号隔开的列表,下面的示例将日志事件输出到控制台和文件:

1

handlers=java.util.logging.ConsoleHandler, java.util.logging.FileHandler

2)如果使用基于XML的配置文件,Appenders会被添加到<Appenders>元素下面,如果使用Log4j,我们可以很容易地添加一个新ConsoleAppender来将日志消息发送到System.out:

1

2

3

<Console name="console" target="SYSTEM_OUT">

  <PatternLayout pattern="[%p] %t: %m%n" />

</Console>

2、Appenders类型

  • 1)ConsoleAppender是最常用的Appenders之一,它只是将日志消息显示到控制台上。许多日志框架都将其作为默认的Appender,并且在基本的配置中进行预配置。
  • 2)FileAppenders将日志记录写入到文件中,它负责打开、关闭文件,向文件中追加日志记录,并对文件进行加锁,以免数据被破坏或者覆盖
  • 3)SyslogAppenders将日志记录发送给本地或者远程系统的日志服务syslog是一个接收日志事件服务,这些日志事件来自操作系统、进程、其它服务或者其它设备。事件的范围可以从诊断信息到用户登录硬件失败等。syslog的事件按照设备进行分类,它指定了正在记录的事件的类型。例如,auth facility表明这个事件是和安全以及认证有关。
  • 4)其它Appender。还有很多其它Appender,它们添加了新功能或者在其它的一些Appender基础上实现了新功能。例如,Log4j中的RollingFileAppender扩展了FileAppender,它可以在满足特定条件时自动创建新的日志文件;SMTPAppender会将日志内容以邮件的形式发送出去;FailoverAppender会在处理日志的过程中,如果一个或者多个Appender失败,自动切换到其他Appender上。

Layouts

  • Layouts将日志记录的内容从一种数据形式转换成另外一种。日志框架为纯文本、HTML、syslog、XML、JSON、序列化以及其它日志提供了Layouts。
  • 请注意在java.util.logging中Layouts也被称为Formatters。java.util.logging提供了两种Layouts:SimpleFormatter和XMLFormatter。
  • 默认情况下,ConsoleHandlers使用SimpleFormatter,它输出的纯文本日志记录就像这样:

1

2

Mar 31, 2015 10:47:51 AM MyClass main

SEVERE: An exception occurred.

  • 默认情况下,FileHandlers使用XMLFormatter,它的输出就像这样:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<!DOCTYPE log SYSTEM "logger.dtd">

<log>

<record>

  <date>2015-03-31T10:47:51</date>

  <millis>1427903275893</millis>

  <sequence>0</sequence>

  <logger>MyClass</logger>

  <level>SEVERE</level>

  <class>MyClass</class>

  <method>main</method>

  <thread>1</thread>

  <message>An exception occurred.</message>

</record>

</log>

1、配置Layout

  • 通常使用配置文件对Layouts进行配置从Java 7开始,也可以使用system property来配置SimpleFormatter。

例如,在Log4j和Logback中最常用的Layouts是PatternLayout。它可以让你决定日志事件中的哪些部分需要输出,这是通过转换模式(Conversion Pattern)完成的,转换模式在每一条日志事件的数据中扮演了“占位符”的角色。例如,Log4j默认的PatternLayout使用了如下转换模式:

1

<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>

%d{HH:mm:ss.SSS} 将日期转换成时、分、秒和毫秒的形式,%level显示日志事件的严重程度,%C显示生成日志事件的类的名字,%t显示Logger的当前线程,%m显示时间的消息,最后,%n为下一个日志事件进行了换行。

2、改变Layouts

如果在java.util.logging中使用一个不同的Layout,需要将Appender的formatter属性设置成你想要的Layout在代码中,你可以:创建一个新的Handler调用setFormatter方法,然后通过logger.AddHandler()方法将Handler放到Logger上面。下面的示例创建了一个ConsoleAppender,它使用XMLFormatter来对日志进行格式化,而不是使用默认的SimpleFormatter:

1

2

3

Handler ch = new ConsoleHandler();

ch.setFormatter(new XMLFormatter());

logger.addHandler(ch);

这样Logger将不再输出纯文本的日志记录,而是输出HTML格式的日志记录

3、使用自定义Layouts

自定义Layouts可以让你指定Appender应该如何输出日志记录从Java SE 7开始,尽管你可以调整SimpleLogger的输出,但有一个限制,即只能够调整简单的纯文本消息。对于更高级的格式,例如HTML或者JSON,你需要一个自定义Layout或者一个单独的框架。

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.logging.FileHandler;
import java.util.logging.Filter;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
//创建Logger对象,创建Handler,设置Handler,把handler加到logger上
public class LogTest {
    static Logger logger = Logger.getLogger(LogTest.class.getName());

    public static void HandlerFilter() throws SecurityException, IOException {
        //声明一个文件处理器,因为打算将日志输出到一个文件当中,即保存在E盘的a.txt文件
        FileHandler handler = new FileHandler("E://a.txt");

        //自定义一个Formatter,用于对每条输出日志进行格式化,不指定则默认使用SimpleFormatter
        handler.setFormatter(new Formatter() {
            @Override
            public String format(LogRecord record) {
                //自定义的格式
                return "[" + new SimpleDateFormat().format(record.getMillis()) + "]-[" + record.getLevel()
                        + "]-" + record.getSourceClassName() + "-" + "[" + record.getMessage() + "]\n";
            }
        });

        //设置一个过滤器
        handler.setFilter(new Filter() {
            @Override
            public boolean isLoggable(LogRecord record) {
                //当日志内容与"this is a unloged message!"相同时,则不输出该日志,当然这里只是测试,实际项目这个规则是不存在的
                return !record.getMessage().equals("this is a unloged message!");
            }
        });

        //给Logger设置Handler
        logger.addHandler(handler); //把设置好格式和过滤器的handler加到logger上面
        logger.setUseParentHandlers(false); //默认是ConsoleHandler,禁用之后才不会输出到控制台,而是输出到文件中
    }

    public static void main(String[] args) throws SecurityException, IOException {
        HandlerFilter();
        logger.log(Level.INFO, "this is a info message!");
        logger.log(Level.OFF, "this is a OFF message!");
        logger.log(Level.SEVERE, "this is a SEVERE message!");
        logger.log(Level.WARNING, "this is a WARNING message!");
        logger.log(Level.INFO, "this is a unloged message!");//这一句将不会被记录到文件中
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值