1.日志
Loggers:被称为记录器,应用程序通过获取Logger对象,调用其API来来发布日志信息。Logger通常时应用程序访问日志系统的入口程序。
Appenders:也被称为Handlers,每个Logger都会关联一组Handlers,Logger会将日志交给关联
Handlers处理,由Handlers负责将日志做记录。Handlers在此是一个抽象,其具体的实现决定了日志记录的位置可以是控制台、文件、网络上的其他日志服务或操作系统日志等。
Layouts:也被称为Formatters,它负责对日志事件中的数据进行转换和格式化。Layouts决定了数据在一条日志记录中的最终形式。
Level:每条日志消息都有一个关联的日志级别。该级别粗略指导了日志消息的重要性和紧迫,我可以将Level和Loggers,Appenders做关联以便于我
们过滤消息。
Filters:过滤器,根据需要定制哪些信息会被记录,哪些信息会被放过。
用户使用Logger来进行日志记录,Logger持有若干个Handler,日志的输出操作是由Handler完成的。
在Handler在输出日志前,会经过Filter的过滤,判断哪些日志级别过滤放行哪些拦截,Handler会将日志内容输出到指定位置(日志文件、控制台等)。Handler在输出日志时会使用Layout,将输出内容进行排版。
1.1 入门案例
package top.onefine;
import org.junit.Test;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JULTest {
// Java获取当前类名的两种方法
// - 适用于非静态方法:this.getClass().getName()
// - 适用于静态方法:Thread.currentThread().getStackTrace()[1].getClassName()
@Test
public void testQuick() {
// 1. 获取日志记录器对象,参数需要一个唯一标识
Logger logger = Logger.getLogger(this.getClass().getName());
// 2. 日志记录输出
logger.info("hello jul"); // 日志级别:info
// 使用通用方法进行日志记录
logger.log(Level.INFO, "info msg");
// 通过占位符的方式输出变量的值
String name = "one fine";
int age = 18;
logger.log(Level.INFO, "用户信息:{0}, {1}", new Object[] {name, age});
}
}
1.2 日志的级别
jul中定义的日志级别
* java.util.logging.Level中定义了日志的级别:
SEVERE(最高值) # 严重
WARNING # 警告
INFO (默认级别) # 信息
CONFIG # 配置
FINE # 详细
FINER # 较详细
FINEST(最低值) # 非常详细
* 还有两个特殊的级别:
OFF,可用来关闭日志记录。
ALL,启用所有消息的日志记录。
虽然我们测试了7个日志级别但是默认只实现info以上的级别
package top.onefine;
import org.junit.Test;
import java.util.logging.Logger;
public class JULTest {
@Test
public void testLogLevel() {
// 1. 获取日志记录器对象,参数需要一个唯一标识
Logger logger = Logger.getLogger(this.getClass().getName());
// 2. 日志记录输出
logger.severe("severe");
logger.warning("warning");
logger.info("info");
// 默认情况下后面将不会输出到控制台(被过滤)
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
}
}
自定义日志级别配置
package top.onefine;
import org.junit.Test;
import java.io.IOException;
import java.util.logging.*;
public class JULTest {
// 自定义日志级别
@Test
public void testLogConfig() throws IOException {
// 1. 获取日志记录器对象,参数需要一个唯一标识
Logger logger = Logger.getLogger(this.getClass().getName());
// 关闭系统默认配置
logger.setUseParentHandlers(false);
/* 自定义配置日志级别
*/
// 创建ConsoleHandler 控制台输出
ConsoleHandler consoleHandler = new ConsoleHandler();
// 创建简单格式formatter转换对象
SimpleFormatter simpleFormatter = new SimpleFormatter();
// 进行关联
consoleHandler.setFormatter(simpleFormatter);
logger.addHandler(consoleHandler);
// 配置日志具体级别
logger.setLevel(Level.ALL);
consoleHandler.setLevel(Level.ALL);
/* 输出日志到文件
*/
// 创建FileHandler 文件输出
FileHandler fileHandler = new FileHandler("d:/logs/jul.log");// 参数为日志文件放置位置,注意处理异常
// 进行关联
fileHandler.setFormatter(simpleFormatter);
logger.addHandler(fileHandler);
// 配置日志具体级别
logger.setLevel(Level.ALL);
consoleHandler.setLevel(Level.ALL);
// 2. 日志记录输出
logger.severe("severe");
logger.warning("warning");
logger.info("info");
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
}
}
1.3 Logger之间的父子关系
JUL中Logger之间存在父子关系,这种父子关系通过树状结构存储,JUL在初始化时会创建一个顶层RootLogger作为所有Logger父Logger,存储上作为树状结构的根节点。并父子关系通过路径来关联。
package top.onefine;
import org.junit.Test;
import java.util.logging.*;
public class JULTest {
// Logger对象父子关系
@Test
public void testLogParent() {
Logger logger1 = Logger.getLogger("top.onefine");
Logger logger2 = Logger.getLogger("top");
System.out.println("logger2" + ((logger1.getParent() == logger2) ? "是" : "不是") + "logger1的父亲"); // true
System.out.println("logger2 parent: " + logger2.getParent() + ", " +
"name: " + logger2.getParent().getName());
/* 输出:
logger2是logger1的父亲
logger2 parent: java.util.logging.LogManager$RootLogger@79bc6541, name:
*/
// LogManager$RootLogger是所有日志记录器的顶级父元素,name为空串""
/* 配置logger2的日志级别
*/
// 关闭logger2默认配置
logger2.setUseParentHandlers(false);
// 创建ConsoleHandler 控制台输出
ConsoleHandler consoleHandler = new ConsoleHandler();
// 创建简单格式formatter转换对象
SimpleFormatter simpleFormatter = new SimpleFormatter();
// 进行关联
consoleHandler.setFormatter(simpleFormatter);
logger2.addHandler(consoleHandler);
// 配置日志具体级别
logger2.setLevel(Level.ALL);
consoleHandler.setLevel(Level.ALL);
logger1.severe("severe");
logger1.warning("warning");
logger1.info("info");
logger1.config("config");
logger1.fine("fine");
logger1.finer("finer");
logger1.finest("finest");
}
}
结果:
logger2是logger1的父亲
logger2 parent: java.util.logging.LogManager$RootLogger@79bc6541, name:
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
严重: severe
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
严重: severe
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
警告: warning
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
警告: warning
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
信息: info
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
信息: info
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
配置: config
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
详细: fine
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
较详细: finer
五月 16, 2020 11:17:37 下午 top.onefine.JULTest testLogParent
非常详细: finest
1.4 日志的配置文件
默认配置文件路径$JAVAHOME\jre\lib\logging.properties:
// C:\Program Files\Java\jre1.8.0_191\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
配置文件resources/logging.properties:
# 指定RootLogger(顶级父类元素)的处理器(获取时设置)为:ConsoleHandler和FileHandler 即同时指定
#handlers= java.util.logging.ConsoleHandler, java.util.logging.FileHandler
# RootLogger顶级父类元素的日志等级:ALL
.level= ALL
## 文件处理器handle对象
# 输出日志级别
java.util.logging.FileHandler.pattern = d:/logs/java%u.log
# 输出日志文件限制大小(50000字节)
java.util.logging.FileHandler.limit = 50000
# 输出日志文件限制个数,5表示:0 - 4
java.util.logging.FileHandler.count = 1
# 以追加的方式添加日志内容,默认false
java.util.logging.FileHandler.append = true
# 输出日志格式,这里是XML
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
#java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
## 控制台处理器handle对象
# 输出日志级别
java.util.logging.ConsoleHandler.level = ALL
# 指定输出日志消息格式对象
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 指定此控制台处理器handler对象的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
# 指定日志消息格式:五月 17, 2020 8:16:07 上午 top.onefine.JULTest testLogProperties 默认
#java.util.logging.ConsoleHandler.format = %4$s: %5$s [%1$tc]%n
## 自定义Logger, name = top.onefine
# 指定处理器
top.onefine.handlers = java.util.logging.ConsoleHandler
# 设置日志级别
top.onefine.level = CONFIG
# 关闭默认配置(忽略父日志设置)
top.onefine.useParentHanlders = false
java/top/onefine/JULTest.java:
package top.onefine;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.*;
public class JULTest {
// 加载自定义配置文件
@Test
public void testLogProperties() throws IOException {
// 读取配置文件,通过类加载器
InputStream ins = JULTest.class.getClassLoader().getResourceAsStream("logging.properties");
// 创建LogManager
LogManager logManager = LogManager.getLogManager();
// 通过LogManager加载配置文件
logManager.readConfiguration(ins); // 注意处理异常
// 创建日志记录器
Logger logger = Logger.getLogger(this.getClass().getName());
logger.severe("severe");
logger.warning("warning");
logger.info("info");
logger.config("config");
logger.fine("fine");
logger.finer("finer");
logger.finest("finest");
}
}
1.5 日志原理解析
初始化LogManager
-LogManager加载logging.properties配置
-添加Logger到LogManager
从单例LogManager获取Logger
设置级别Level,并指定日志记录LogRecord
Filter提供了日志级别之外更细粒度的控制
Handler是用来处理日志输出位置
Formatter是用来格式化LogRecord的
JCL 学习【了解】
JCL全称为Jakarta Commons Logging,是Apache提供的一个通用日志API。
它是为 "所有的Java日志实现"提供一个统一的接口,它自身也提供一个日志的实现,但是功能非常常弱(SimpleLog),所以一般不会单独使用它。他允许开发人员使用不同的具体日志实现工具: Log4j, Jdk自带的日志(JUL)
JCL 有两个基本的抽象类:
Log:基本记录器
LogFactory:负责创建Log实例
Hello World
延续编程领域的传统,这里用一个例子说明使用SLF4J最简单的方式输出"Hello world"。首先,获取一个HelloWorld类的logger日志对象。这个logger对象反过来用于记录"Hello World"消息。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
典型的使用模式
下面的示例代码说明了SLF4J一个典型的使用模式。注意第15行的占位符{}的使用。参考FAQ的问题"What is the fastest way of logging?"了解更多详细信息。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Wombat {
final Logger logger = LoggerFactory.getLogger(Wombat.class);
Integer t;
Integer oldT;
public void setTemperature(Integer temperature) {
oldT = t;
t = temperature;
logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);
if(temperature.intValue() > 50) {
logger.info("Temperature has risen above 50 degrees.");
}
}
}
在部署时绑定日志框架
正如之前提到的,SLF4J支持不同的日志框架。SLF4J发布包附带的几个jar包被称为“SLF4J绑定”,每一个绑定对应一种支持的框架。