一、概述和组件

JUL全称Java util Logging是java原生的日志框架,使用时不需要另外引用第三方类库,相对其他日志框 架使用方便,学习简单,能够在小型应用中灵活使用。

在JUL中有以下组件,我们先做了解,慢慢学习:

  • 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,将输出内容进行排版。

二、入门案例

第一步:创建maven项目

在这里插入图片描述

第二步:pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>logging-test</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <properties>
  	<maven.compiler.source>8</maven.compiler.source>
  	<maven.compiler.target>8</maven.compiler.target>
  </properties>
  
  <dependencies>
  	<dependency>
  		<artifactId>junit</artifactId>
  		<groupId>junit</groupId>
  		<version>4.12</version>
  	</dependency>
  </dependencies>
  
  <build>
  	<plugins>
 		<plugin>
 			<groupId>org.apache.maven.plugins</groupId>
 			<artifactId>maven-compiler-plugin</artifactId>
 			<version>3.8.1</version>
 			<configuration>
 				<source>${maven.compiler.source}</source>
 				<target>${maven.compiler.target}</target>
 				<encoding>UTF-8</encoding>
 			</configuration>
 		</plugin>
  	</plugins>
  </build>
</project>

第三步:测试代码

package com.example.test;

import java.util.logging.Logger;

import org.junit.Test;

public class TestJUL {

	// 获取一个logger对象
	// Logger.getLogger(name)这个name将来我们可以在日志里面通过名字找到具体哪个类里边出现了这个名字,一般是用类的全限定名称
	public static Logger logger = Logger.getLogger("com.example.test.TestJUL");

	@Test
	public void testJULDefaultLevel() {
		// JUL的默认级别是info,默认配置文件在jdk目录里,只有比info级别大的才会打印出来
		logger.severe("严重");
		logger.warning("警告");
		logger.info("信息");
	}
}

第四步:打印结果

在这里插入图片描述

二、日志的级别

jul中定义的日志级别,从上述例子中我们也看到使用info和warning打印出的日志有不同的前缀,通过给日志设置不同的级别可以清晰的从日志中区分出哪些是基本信息,哪些是调试信息,哪些是严重的异常。

(1)java.util.logging.Level中定义了日志的级别:

  1. SEVERE(最高值)
  2. WARNING
  3. INFO(默认级别)
  4. CONFIG
  5. FINE
  6. FINER
  7. FINEST(最低值)

还有两个特殊的级别:

  • OFF,可用来关闭日志记录。
  • ALL,启用所有消息的日志记录。

虽然我们测试了7个日志级别,

package com.example.test;

import java.io.IOException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.junit.Test;

public class TestJUL {

	// 获取一个logger对象
	// Logger.getLogger(name)这个name将来可以我们在日志里面通过名字找到具体哪个类里边出现了这个名字,一般是用类的全限定名称
	public static Logger logger = Logger.getLogger("com.example.test.TestJUL");

	@Test
	public void testJULAllLevel() {
		// JUL的默认级别为info,只有比info级别大的才会打印出来
		logger.severe("severe");
		logger.warning("warning");
		logger.info("info");
		logger.config("config");
		logger.fine("fine");
		logger.finer("finer");
		logger.finest("finest");
	}
}

我们发现能够打印的只有三行,这是为什么呢?

在这里插入图片描述

我们找一下这个文件,下图是jdk11的日志配置文件:

在这里插入图片描述

或者在jdk1.8中:

在这里插入图片描述

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

在日志中我们发现了,貌似可以给这个日志对象添加各种handler就是处理器,比如ConsoleHandler专门处理控制台日志,FileHandler貌似可以处理文件,同时我们确实发现了他有这么一个方法:

在这里插入图片描述

解决方案

  1. 可以通过配置文件修改,但尽量不要去修改
  2. 通过编程式配置修改
package com.example.test;

import java.io.IOException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.junit.Test;

public class TestJUL {

	// 获取一个logger对象
	// Logger.getLogger(name)这个name将来可以我们在日志里面通过名字找到具体哪个类里边出现了这个名字,一般是用类的全限定名称
	public static Logger logger = Logger.getLogger("com.example.test.TestJUL");
	
	@Test
	public void testJULShowAllLevel() {
		// JUL的默认级别为info,只有比info级别大的才会打印出来
		// 我们可以修改配置文件,但我们不动配置文件,通过编程式配置来配置下级别

		// a、关闭系统默认配置
		logger.setUseParentHandlers(false);
		// b、创建handler对象

		ConsoleHandler consoleHandler = new ConsoleHandler();
		// c.创建formatter对象
		SimpleFormatter simpleFormatter = new SimpleFormatter();
		// d.进行关联
		consoleHandler.setFormatter(simpleFormatter);
		logger.addHandler(consoleHandler);
		// e.设置日志级别
		logger.setLevel(Level.ALL);
		consoleHandler.setLevel(Level.ALL);

		logger.severe("severe");
		logger.warning("warning");
		logger.info("info");
		logger.config("config");
		logger.fine("fine");
		logger.finer("finer");
		logger.finest("finest");
	}
}

在这里插入图片描述

三、日志配置

package com.example.test;

import java.io.IOException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.junit.Test;

public class TestJUL {

	// 获取一个logger对象
	// Logger.getLogger(name)这个name将来可以我们在日志里面通过名字找到具体哪个类里边出现了这个名字,一般是用类的全限定名称
	public static Logger logger = Logger.getLogger("com.example.test.TestJUL");


	@Test
	public void testJULAddHandler() throws SecurityException, IOException {
		/**
		 * 日志可以输出到各种数据源,比如文件,控制台,数据库,内存等 可以看java.util.logging.Handler的子类
		 * 发现他有FileHandler,MemoryHandler,SLF4JBridgeHandler等等
		 * 
		 * 我们想控制台打印全部级别,文件只打印warn级别
		 */

		// a、关闭系统默认配置
		logger.setUseParentHandlers(false);
		// b、创建handler对象

		ConsoleHandler consoleHandler = new ConsoleHandler();
		// c.创建formatter对象
		SimpleFormatter simpleFormatter = new SimpleFormatter();
		// d.进行关联
		consoleHandler.setFormatter(simpleFormatter);
		logger.addHandler(consoleHandler);
		// e.设置日志级别
		logger.setLevel(Level.ALL);
		consoleHandler.setLevel(Level.ALL);

		// 二、输出到日志文件
		FileHandler fileHandler = new FileHandler("d:/logs/jul.log",true);
		fileHandler.setFormatter(simpleFormatter);
		fileHandler.setLevel(Level.WARNING);
		logger.addHandler(fileHandler);
		
		
		logger.severe("severe");
		logger.warning("warning");
		logger.info("info");
		logger.config("config");
		logger.fine("fine");
		logger.finer("finer");
		logger.finest("finest");

		/*
		 * 总结:对于我们的logger来说,我logger所有级别的日志都接受,我分配给不同的handler, 不同的handler可以进行筛选,通过level筛选
		 * 
		 * 我们的控制台全打印,而对于文件来说,只打印warn级别以上
		 */
	}
}

在这里插入图片描述

在这里插入图片描述

四、 Logger之间的父子关系

JUL中Logger之间存在父子关系,这种父子关系通过树状结构存储,JUL在初始化时会创建一个顶层 RootLogger作为所有Logger父Logger,存储上作为树状结构的根节点。并父子关系通过名称来关联。默认子Logger会继承父Logger的属性。

在这里插入图片描述

所有的logger实例都是由LoggerManager统一管理,不妨我们点进Logger LOGGER = Logger.getLogger(“com.example.test.TestJUL”);方法:

在这里插入图片描述

我们可以看到LogManager是单例的:
在这里插入图片描述

package com.example.test;

import java.io.IOException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.junit.Test;

public class TestJUL {

	// 获取一个logger对象
	// Logger.getLogger(name)这个name将来可以我们在日志里面通过名字找到具体哪个类里边出现了这个名字,一般是用类的全限定名称
	public static Logger logger = Logger.getLogger("com.example.test.TestJUL");
	
	@Test
	public void testParent() {
		//任何一个logger都是单例的,名字相同的只有一个
		Logger LOGGER = Logger.getLogger("com.example.test.TestJUL");
		System.out.println(LOGGER == logger);
		
		//【a】获取LOGGER的父类
		Logger parent = LOGGER.getParent();
		System.out.println(parent);
		
		//【b】创建com.example.test的logger
		Logger testLogger = Logger.getLogger("com.example.test");
		parent = LOGGER.getParent();
		System.out.println(parent.getName());
		System.out.println(testLogger.getParent());
		/**
		 * 运行结果发现在进行【a】操作时,com.example.test.TestJUL的父类是rootLogger
		 * 进行【b】操作时,创建了com.example.test的logger,在打印com.example.test.TestJUL的父类时,
		 * 发现他的父类为com.example.test。创建的com.example.test的父类是rootLogger了
		 * 
		 * 总结:以.来完成日志的继承关系
		 */
	}
	
	@Test
	public void testParent2() {
		//任何一个logger都是单例的,名字相同的只有一个
		Logger LOGGER = Logger.getLogger("com.example.test.TestJUL");
		
		//我们创建的logger没有任何其他的配置,一定会遵循rootLogger的配置,rootLogger的配置级别为info,目前打印也是只有info及以上才可以打印
		Logger testLogger = Logger.getLogger("com.example.test");
		
		testLogger.severe(">>>>>>>>>>>>>>severe");
		testLogger.warning(">>>>>>>>>>>>>>warning");
		testLogger.info(">>>>>>>>>>>>>>info");
		testLogger.fine(">>>>>>>>>>>>>>fine");
		testLogger.finer(">>>>>>>>>>>>>>finer");
		testLogger.finest(">>>>>>>>>>>>>>finest");
		
		LOGGER.severe("severe");
		LOGGER.warning("warning");
		LOGGER.info("info");
		LOGGER.fine("fine");
		LOGGER.finer("finer");
		LOGGER.finest("finest");
	}
	
	@Test
	public void testParent3() {
		/**
		 * 给com.example.test.TestJUL的亲生父亲做些配置,看我com.example.test.TestJUL能否遗传到
		 */
		
		Logger parentLogger = Logger.getLogger("com.example.test");
		//是否使用parent的配置,这个不设置会多打印一次
		parentLogger.setUseParentHandlers(false);
		parentLogger.setLevel(Level.ALL);
		ConsoleHandler consoleHandler = new ConsoleHandler();
		consoleHandler.setLevel(Level.ALL);
		consoleHandler.setFormatter(new SimpleFormatter());
		parentLogger.addHandler(consoleHandler);
		
		
		Logger childLogger = Logger.getLogger("com.example.test.TestJUL");
		
		childLogger.severe("severe");
		childLogger.warning("warning");
		childLogger.info("info");
		childLogger.fine("fine");
		childLogger.finer("finer");
		childLogger.finest("finest");
		
	}
}

testParent运行结果图
在这里插入图片描述

testParent2运行结果图

在这里插入图片描述

testParent3运行结果图

在这里插入图片描述

五、日志格式化

我们可以独立的实现日志格式化的Formatter,而不使用SimpleFormatter,我们可以做如下处理,最后返回的结果我们可以随意拼写:

package com.example.test;

import java.io.IOException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.junit.Test;

public class TestJUL {

	// 获取一个logger对象
	// Logger.getLogger(name)这个name将来可以我们在日志里面通过名字找到具体哪个类里边出现了这个名字,一般是用类的全限定名称
	public static Logger logger = Logger.getLogger("com.example.test.TestJUL");
	
	
	@Test
	public void testFormatter() {
		// JUL的默认级别为info,只有比info级别大的才会打印出来
		// 我们可以修改配置文件,但我们不动配置文件,通过编程式配置来配置下级别

		// a、关闭系统默认配置
		logger.setUseParentHandlers(false);
		// b、创建handler对象
		ConsoleHandler consoleHandler = new ConsoleHandler();
		// c.创建formatter对象
		consoleHandler.setFormatter(new Formatter() {
			
			@Override
			public String format(LogRecord record) {
				return record.getLoggerName() + "  " + record.getMessage() + "\r\n";
			}
		});
		// d.进行关联
		logger.addHandler(consoleHandler);
		// e.设置日志级别
		logger.setLevel(Level.ALL);
		consoleHandler.setLevel(Level.ALL);

		logger.severe("severe");
		logger.warning("warning");
		logger.info("info");
		logger.config("config");
		logger.fine("fine");
		logger.finer("finer");
		logger.finest("finest");
	}

}

在这里插入图片描述

六、配置文件

1、源码找配置文件

第一步:进入getLogger方法

先点击Logger testLogger = Logger.getLogger(“com.example.test”);,进入getLogger方法

在这里插入图片描述

第二步:进入demandLogger方法

在这里插入图片描述

第三步:进入getLogManager方法

在这里插入图片描述

第四步:进入ensureLogManagerInitialized方法

往下滑,我们会看到owner.readPrimordialConfiguration();

在这里插入图片描述

第五步:进入readPrimordialConfiguration方法

在这里插入图片描述

第六步:进入readConfiguration方法

在这里插入图片描述

从这里我们可以看到JUL会找属性名java.util.logging.config.class的值,发现找不到则找属性名java.util.logging.config.file,发现这个属性值也为空,则会找属性名为java.home,这个属性值就是我们配置jdk的JAVA_HOME的值。同时我们发现这个readConfiguration他重载方法,可以让我们使用自己配置的配置文件。

2、使用自己配置的配置文件

第一步:创建log.properties
handlers= java.util.logging.ConsoleHandler
.level= FINE

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 = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
第二步:测试代码
package com.example.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.junit.Test;

public class TestJUL {

	// 获取一个logger对象
	// Logger.getLogger(name)这个name将来可以我们在日志里面通过名字找到具体哪个类里边出现了这个名字,一般是用类的全限定名称
	public static Logger logger = Logger.getLogger("com.example.test.TestJUL");
	
	@Test
	public void testReadConfiguration() throws SecurityException, IOException {
		LogManager logManager = LogManager.getLogManager();
		//读取配置文件
		InputStream iStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("log.properties");
		logManager.readConfiguration(iStream);
		
		logger.fine("fine");
		logger.finer("finer");
	}
}

在这里插入图片描述

3、配置文件使用

我们可以在配置文件中同时使用多个Handler

第一步:配置log.properties
# rootLogger
handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler
.level= FINE

#%h表示user.home,可以看FileHandler构造方法
#java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.pattern = d:/logs/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 = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
第二步:测试代码
package com.example.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.junit.Test;

public class TestJUL {

	// 获取一个logger对象
	// Logger.getLogger(name)这个name将来可以我们在日志里面通过名字找到具体哪个类里边出现了这个名字,一般是用类的全限定名称
	public static Logger logger = Logger.getLogger("com.example.test.TestJUL");
	
	@Test
	public void testReadConfiguration() throws SecurityException, IOException {
		LogManager logManager = LogManager.getLogManager();
		//读取配置文件
		InputStream iStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("log.properties");
		logManager.readConfiguration(iStream);
		
		logger.fine("fine");
		logger.finer("finer");
	}
}

控制台打印
在这里插入图片描述

文件打印

在这里插入图片描述

当我们多次运行testReadConfiguration单元测试方法时,我们发现他写文件方式不是追加。如果我们想要追加,则在配置文件里添加java.util.logging.FileHandler.append=true

看java.util.logging.FileHandler类我们会发现,配置文件中有部分是使用FileHandler的全限定名+小数点+成员变量

在这里插入图片描述

FileHandler源码看构造方法

4、独立日志配置

第一步:修改log.properties
# rootLogger
handlers= java.util.logging.ConsoleHandler
.level= FINE

#配置com.example.test的logger
com.example.test.handlers= java.util.logging.ConsoleHandler
com.example.test.level= WARNING

#%h表示user.home,可以看FileHandler构造方法
#java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.pattern = d:/logs/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.FileHandler.append=true

java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
第二步:编写测试代码
package com.example.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.junit.Test;

public class TestJUL {

	// 获取一个logger对象
	// Logger.getLogger(name)这个name将来可以我们在日志里面通过名字找到具体哪个类里边出现了这个名字,一般是用类的全限定名称
	public static Logger logger = Logger.getLogger("com.example.test.TestJUL");
	
	@Test
	public void testCustomLoggerReadConfiguration() throws SecurityException, IOException {
		LogManager logManager = LogManager.getLogManager();
		//读取配置文件
		InputStream iStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("log.properties");
		logManager.readConfiguration(iStream);
		System.out.println(logger.getParent());
		
		Logger LOGGER = Logger.getLogger(TestJUL.class.getName());
		LOGGER.fine("fine");
		LOGGER.finer("finer");
	}
}

运行我们发现:自定义配置的logger并没有产生效果
在这里插入图片描述

原因

  1. 在TestJUL类加载时,静态常量logger被加载,此时并没有加载log.properties
  2. 拿到com.example.test.TestJUL logger
  3. testCustomLoggerReadConfiguration方法里读取了配置文件,rootLogger被修改。

解决方案:

package com.example.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.junit.Test;

public class TestJUL {
	
	static {
		LogManager logManager = LogManager.getLogManager();
		//读取配置文件
		InputStream iStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("log.properties");
		try {
			logManager.readConfiguration(iStream);
		} catch (SecurityException | IOException e) {
			e.printStackTrace();
		}
	}

	// 获取一个logger对象
	// Logger.getLogger(name)这个name将来可以我们在日志里面通过名字找到具体哪个类里边出现了这个名字,一般是用类的全限定名称
	public static Logger logger = Logger.getLogger("com.example.test.TestJUL");
	
	@Test
	public void testCustomLoggerReadConfiguration() throws SecurityException, IOException {
		//1、拿到com.example.test.TestJUL logger
		//2、读取了配置文件,rootLogger被修改
		
		System.out.println(logger.getParent());
		
		Logger LOGGER = Logger.getLogger(TestJUL.class.getName());
		LOGGER.fine("fine");
		LOGGER.finer("finer");
		System.out.println(LOGGER==logger);
		
//		logger.fine("fine");
//		logger.finer("finer");
	}
}

运行结果图

在这里插入图片描述

5、修改打印日志格式

第一步:修改log.properties
# rootLogger
handlers= java.util.logging.ConsoleHandler
.level= FINE

#配置com.example.test的logger
com.example.test.handlers= java.util.logging.ConsoleHandler
com.example.test.level= WARNING

#%h表示user.home,可以看FileHandler构造方法
#java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.pattern = d:/logs/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.FileHandler.append=true

java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n
第二步:测试类
package com.example.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.junit.Test;

public class TestJUL {
	
	static {
		LogManager logManager = LogManager.getLogManager();
		//读取配置文件
		InputStream iStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("log.properties");
		try {
			logManager.readConfiguration(iStream);
		} catch (SecurityException | IOException e) {
			e.printStackTrace();
		}
	}

	// 获取一个logger对象
	// Logger.getLogger(name)这个name将来可以我们在日志里面通过名字找到具体哪个类里边出现了这个名字,一般是用类的全限定名称
	public static Logger logger = Logger.getLogger("com.example.test.TestJUL");
	
	@Test
	public void testCustomLoggerReadConfigurationCustomLogFormat() throws SecurityException, IOException {
		//1、拿到com.example.test.TestJUL logger
		//2、读取了配置文件,rootLogger被修改
		
		System.out.println(logger.getParent());
		
		Logger LOGGER = Logger.getLogger(TestJUL.class.getName());
		LOGGER.fine("fine");
		LOGGER.finer("finer");
		LOGGER.warning("warning");
		System.out.println(LOGGER==logger);
		
//		logger.fine("fine");
//		logger.finer("finer");
	}
}

运行结果

在这里插入图片描述

七、过滤器

package com.example.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.junit.Test;

public class TestJUL {

	// 获取一个logger对象
	// Logger.getLogger(name)这个name将来可以我们在日志里面通过名字找到具体哪个类里边出现了这个名字,一般是用类的全限定名称
	public static Logger logger = Logger.getLogger("com.example.test.TestJUL");
	
	@Test
	public void testFilter() {
		// JUL的默认级别为info,只有比info级别大的才会打印出来
		// 我们可以修改配置文件,但我们不动配置文件,通过编程式配置来配置下级别

		// a、关闭系统默认配置
		logger.setUseParentHandlers(false);
		// b、创建handler对象

		ConsoleHandler consoleHandler = new ConsoleHandler();
		// c.创建formatter对象
		SimpleFormatter simpleFormatter = new SimpleFormatter();
		// d.进行关联
		consoleHandler.setFormatter(simpleFormatter);
		logger.addHandler(consoleHandler);
		// e.设置日志级别
		logger.setLevel(Level.ALL);
		consoleHandler.setLevel(Level.ALL);
		
		//设置过滤器
		logger.setFilter(logRecord -> logRecord.getMessage().contains("饿了"));

		logger.severe("severe");
		logger.warning("warning");
		logger.info("in饿了fo");
		logger.config("config");
		logger.fine("fine");
		logger.finer("finer");
		logger.finest("finest");
	}
}

八、打印异常堆栈

第一步:修改log.properties

# rootLogger
handlers= java.util.logging.ConsoleHandler
.level= FINE

#配置com.example.test的logger
com.example.test.handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler
com.example.test.level= FINER
#不继承父类的handlers
com.example.test.useParentHandlers=false

#%h表示user.home,可以看FileHandler构造方法
#java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.pattern = d:/logs/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.FileHandler.append=true
java.util.logging.FileHandler.LEVEL = FINER

java.util.logging.ConsoleHandler.level = FINER
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
#看SimpleFormatter发现第6个参数是throwing
#java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc] %6%s%n

第二步:测试代码

进入到logger.throwing方法会发现,需要设置日志级别为:FINER才能打印。同时如果我们设置自定义打印格式,需要打印异常时 需要添加%6%s

在这里插入图片描述

在这里插入图片描述

package com.example.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.junit.Test;

public class TestJUL {
	
	static {
		LogManager logManager = LogManager.getLogManager();
		//读取配置文件
		InputStream iStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("log.properties");
		try {
			logManager.readConfiguration(iStream);
		} catch (SecurityException | IOException e) {
			e.printStackTrace();
		}
	}

	// 获取一个logger对象
	// Logger.getLogger(name)这个name将来可以我们在日志里面通过名字找到具体哪个类里边出现了这个名字,一般是用类的全限定名称
	public static Logger logger = Logger.getLogger("com.example.test.TestJUL");
	
	@Test
	public void testException() {
		logger.warning("warning");
		logger.fine("fine");
		
		try {
			int i = 1/0;
		} catch (Exception e) {
			logger.throwing(TestJUL.class.getName(), "testException", e);
		}
		
	}
}

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐