更多 Java 基础知识方面的文章,请参见文集《Java 基础知识》
Java日志组件
Java 日志 API 由以下三个核心组件组成:
Loggers:Logger 负责捕捉事件并将其发送给合适的 Appender。
Appenders:也被称为 Handlers,负责将日志事件记录到目标位置。在将日志事件输出之前, Appenders 使用Layouts来对事件进行格式化处理。
Layouts:也被称为 Formatters,它负责对日志事件中的数据进行转换和格式化。Layouts 决定了数据在一条日志记录中的最终形式。
当 Logger 记录一个事件时,它将事件转发给适当的 Appender。然后 Appender 使用 Layout 来对日志记录进行格式化,并将其发送给控制台、文件或者其它目标位置。另外,Filters 可以让你进一步指定一个 Appender 是否可以应用在一条特定的日志记录上。在日志配置中,Filters 并不是必需的,但可以让你更灵活地控制日志消息的流动。
日志框架
1. java.util.logging
默认的 Java 日志框架。
2. 第三方框架
例如:
- Log4j
- Logback
3. 抽象层开发包
例如:
- SLF4J
- Apache Commons Logging
诸如 SLF4J 这样的抽象层,会将你的应用程序从日志框架中解耦。
应用程序可以在运行时选择绑定到一个特定的日志框架(例如 java.util.logging、Log4j 或者 Logback),这通过在应用程序的类路径中添加对应的日志框架来实现。
如果在类路径中配置的日志框架不可用,抽象层就会立刻取消调用日志的相应逻辑。
抽象层可以让我们更加容易地改变项目现有的日志框架,或者集成那些使用了不同日志框架的项目。
《阿里巴巴Java开发手册(正式版)》 中提出:
应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Abc.class);
日志级别 Log Levels
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)
Loggers
创建新Logger
通过 Logger.getLogger()
方法创建新 Logger
,这个方法接收一个 String
参数,用于指定 Logger
的名字。
- 如果指定名字的
Logger
已经存在,那么只需要返回已经存在的Logger
; - 否则,程序会创建一个新
Logger
。
通常情况下,一种好的做法是,我们在当前类下使用 class.getName()
作为新 Logger
的名字。
示例:
Logger logger = Logger.getLogger(MyClass.class.getName());
记录日志事件
logger.log()
方法除了日志消息以外,还需要一个日志级别作为参数:
logger.log(Level.WARNING, "This is a warning!");
大部分日志框架都针对输出特定级别日志提供了快捷方式。例如,下面语句的作用和上面语句的作用是一样的:
logger.warning("This is a warning!");
你还可以阻止 Logger
输出低于指定日志级别的消息。在下面的示例中,Logger
只能输出 等于或者高于 WARNING
级别的日志消息,并丢弃日志级别低于 WARNING
的消息:
logger.setLevel(Level.WARNING);
Appenders
Appenders 将日志消息转发给期望的输出。它负责接收日志事件,使用 Layout 格式化事件,然后将其发送给对应的目标。
对于一个日志事件,我们可以使用多个 Appenders 来将事件发送到不同的目标位置。
例如,我们可以在控制台上显示一个简单的日志事件的同时,将其通过邮件的方式发送给指定的接收者。
增加 Appender
- 如果使用
java.util.logging
,你可以使用logger.addHandler()
方法将 Appender 添加到Logger
中。例如,下面的代码添加了一个新的ConsoleHandler
,它会将日志输出到控制台:
logger.addHandler(new ConsoleHandler());
- 使用配置文件:
- 如果使用
java.util.logging
,Appenders 会定义一个以逗号隔开的列表,下面的示例将日志事件输出到控制台和文件:
handlers=java.util.logging.ConsoleHandler, java.util.logging.FileHandler
- 如果使用 Log4j,可以通过 XML 形式:
- 如果使用
Appenders类型
1. ConsoleAppender
它只是将日志消息显示到控制台上。许多日志框架都将其作为默认的 Appender,并且在基本的配置中进行预配置。
一个完整的 Log4j2 的配置文件如下所示:
你也可以使用 Logback 实现完全一样的效果:
%m%n
2. FileAppenders
将日志记录写入到文件中,它负责打开、关闭文件,向文件中追加日志记录,并对文件进行加锁,以免数据被破坏或者覆盖。
Log4j 示例:
Logback 示例:
myLog.log
true
true
%m%n
3. SyslogAppender
将日志记录发送给本地或者远程系统的日志服务。
syslog 是一个接收日志事件服务,这些日志事件来自操作系统、进程、其它服务或者其它设备。事件的范围可以从诊断信息到用户登录硬件失败等。syslog的事件按照设备进行分类,它指定了正在记录的事件的类型。
Log4j 示例:
Logback 示例:
localhost
514
Auth
4. 其它 Appender
例如,Log4j 中的 RollingFileAppender
扩展了 FileAppender
,它可以在满足特定条件时自动创建新的日志文件;
SMTPAppender
会将日志内容以邮件的形式发送出去;
FailoverAppender
会在处理日志的过程中,如果一个或者多个 Appender 失败,自动切换到其他 Appender上。
如果想了解更多关于其他 Appender 的信息,可以查看 Log4j Appender参考 以及 Logback Appender参考。
Layouts
Layouts 将日志记录的内容从一种数据形式转换成另外一种。
日志框架为纯文本、HTML、syslog、XML、JSON、序列化以及其它日志提供了 Layouts。
例如,java.util.logging
提供了两种 Layouts:SimpleFormatter
和 XMLFormatter
。
- 默认情况下,
ConsoleHandlers
使用SimpleFormatter
- 默认情况下,
FileHandlers
使用XMLFormatter
配置Layout
在 Log4j 和 Logback 中最常用的 Layouts 是 PatternLayout。它可以让你决定日志事件中的哪些部分需要输出,这是通过 转换模式 Conversion Pattern 完成的,转换模式在每一条日志事件的数据中扮演了占位符的角色。
例如,Log4j 默认的 PatternLayout 使用了如下转换模式:
PatternLayout pattern = "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"
%d{HH:mm:ss.SSS}
将日期转换成时、分、秒和毫秒的形式,%level
显示日志事件的严重程度,%C
显示生成日志事件的类的名字,%t
显示 Logger 的当前线程,%m
显示时间的消息,最后,%n
为下一个日志事件进行了换行。
改变Layouts
java.util.logging
中的示例代码如下:
Handler ch = new ConsoleHandler();
ch.setFormatter(new XMLFormatter());
logger.addHandler(ch);
转换模式 Conversion Patterns
Log4j 和 Logback 中的 PatternLayout 类都支持转换模式,它决定了我们如何从每一条日志事件中提取信息以及如何对信息进行格式化。
- 消息
%m
- 级别/严重程度
%p
- 异常
%ex
- 线程
%t
- Logger
%c
- 方法
%M
一个 java.util.logging 完整的示例
Java 代码:
import java.util.logging.Logger;
public class LoggerTest {
public static void main(String[] args) {
Logger logger = Logger.getLogger(MyClass.class.getName());
logger.finest("this is finest");
logger.finer("this is finer");
logger.fine("this is fine");
logger.config("this is config");
logger.info("this is info");
logger.warning("this is a warning");
logger.severe("this is severe");
}
}
默认的 java.util.logging
配置:JRE_DIRECTORY/lib/logging.properties
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
因此默认输出如下:注意配置,只输出 等于或者高于 INFO
级别的日志消息
Feb 13, 2017 3:18:52 PM basic.LoggerTest main
INFO: this is info
Feb 13, 2017 3:18:52 PM basic.LoggerTest main
WARNING: this is a warning
Feb 13, 2017 3:18:52 PM basic.LoggerTest main
SEVERE: this is severe
自定义配置文件 logging.properties
:
handlers=java.util.logging.FileHandler
.level= ALL
java.util.logging.FileHandler.pattern = logger_test.xml
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
运行参数:java LoggerTest -Djava.util.logging.config.file=logging.properties
因此输出到 XML 文件:
-
-
2017-02-13T15:37:25
1486971445252
0
basic.MyClass
FINEST
basic.LoggerTest
main
1
this is finest
-
2017-02-13T15:37:25
1486971445307
1
basic.MyClass
FINER
basic.LoggerTest
main
1
this is finer
-
2017-02-13T15:37:25
1486971445307
2
basic.MyClass
FINE
basic.LoggerTest
main
1
this is fine
-
2017-02-13T15:37:25
1486971445307
3
basic.MyClass
CONFIG
basic.LoggerTest
main
1
this is config
-
2017-02-13T15:37:25
1486971445307
4
basic.MyClass
INFO
basic.LoggerTest
main
1
this is info
-
2017-02-13T15:37:25
1486971445307
5
basic.MyClass
WARNING
basic.LoggerTest
main
1
this is a warning
-
2017-02-13T15:37:25
1486971445307
6
basic.MyClass
SEVERE
basic.LoggerTest
main
1
this is severe
引用:
Java日志终极指南
Java Logging Basics