JUL
JDK提供了一个自带的日志框架,位于java.util.logging
包下,可以使用此框架来实现日志的规范化打印
public class Main {
public static void main(String[] args) {
// 首先获取日志打印器
Logger logger = Logger.getLogger(Main.class.getName());
// 调用info来输出一个普通的信息,直接填写字符串即可
logger.info("我是普通的日志");
}
}
JUL日志
日志分为7个级别:
- SEVERE(最高值)- 一般用于代表严重错误
- WARNING - 一般用于表示某些警告,但是不足以判断为错误
- INFO (默认级别) - 常规消息
- CONFIG
- FINE
- FINER
- FINEST(最低值)
使用info
方法直接输出的结果就是使用默认级别,使用log
方法可以设定该条日志的输出级别
public static void main(String[] args) {
Logger logger = Logger.getLogger(Main.class.getName());
logger.log(Level.SEVERE, "严重的错误", new IOException("我就是错误"));
logger.log(Level.WARNING, "警告的内容");
logger.log(Level.INFO, "普通的信息");
logger.log(Level.CONFIG, "级别低于普通信息");
}
根据输出结果发现最后一个无法输出,因为它的级别低于默认级别,如果需要输出该日志就必须修改日志的打印级别
public static void main(String[] args) {
Logger logger = Logger.getLogger(Main.class.getName());
//修改日志级别
logger.setLevel(Level.CONFIG);
//不使用父日志处理器
logger.setUseParentHandlers(false);
//使用自定义日志处理器
ConsoleHandler handler = new ConsoleHandler();
handler.setLevel(Level.CONFIG);
logger.addHandler(handler);
logger.log(Level.SEVERE, "严重的错误", new IOException("我就是错误"));
logger.log(Level.WARNING, "警告的内容");
logger.log(Level.INFO, "普通的信息");
logger.log(Level.CONFIG, "级别低于普通信息");
}
每个Logger
都有一个父日志打印器,我们可以通过getParent()
来获取:
public static void main(String[] args) throws IOException {
Logger logger = Logger.getLogger(Main.class.getName());
System.out.println(logger.getParent().getClass());
}
我们发现,得到的是java.util.logging.LogManager$RootLogger
这个类,它默认使用的是ConsoleHandler,且日志级别为INFO,由于每一个日志打印器都会直接使用父类的处理器,因此我们之前需要关闭父类然后使用我们自己的处理器。
日志处理器不仅仅只有控制台打印,我们也可以使用文件处理器来处理日志信息,我们继续添加一个处理器
//添加输出到本地文件
FileHandler fileHandler = new FileHandler("test.log");
fileHandler.setLevel(Level.WARNING);
logger.addHandler(fileHandler);
注意,这个时候就有两个日志处理器了,因此控制台和文件的都会生效。如果日志的打印格式我们不喜欢,我们还可以自定义打印格式,比如我们控制台处理器就默认使用的是SimpleFormatter
,而文件处理器则是使用的XMLFormatter
,我们可以自定义:
//使用自定义日志处理器(控制台)
ConsoleHandler handler = new ConsoleHandler();
handler.setLevel(Level.CONFIG);
handler.setFormatter(new XMLFormatter());
logger.addHandler(handler);
我们可以直接配置为想要的打印格式,如果这些格式还不能满足你,那么我们也可以自行实现:
public static void main(String[] args) throws IOException {
Logger logger = Logger.getLogger(Main.class.getName());
logger.setUseParentHandlers(false);
//为了让颜色变回普通的颜色,通过代码块在初始化时将输出流设定为System.out
ConsoleHandler handler = new ConsoleHandler(){{
setOutputStream(System.out);
}};
//创建匿名内部类实现自定义的格式
handler.setFormatter(new Formatter() {
@Override
public String format(LogRecord record) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
String time = format.format(new Date(record.getMillis())); //格式化日志时间
String level = record.getLevel().getName(); // 获取日志级别名称
// String level = record.getLevel().getLocalizedName(); // 获取本地化名称(语言跟随系统)
String thread = String.format("%10s", Thread.currentThread().getName()); //线程名称(做了格式化处理,留出10格空间)
long threadID = record.getThreadID(); //线程ID
String className = String.format("%-20s", record.getSourceClassName()); //发送日志的类名
String msg = record.getMessage(); //日志消息
//\033[33m作为颜色代码,30~37都有对应的颜色,38是没有颜色,IDEA能显示,但是某些地方可能不支持
return "\033[38m" + time + " \033[33m" + level + " \033[35m" + threadID
+ "\033[38m --- [" + thread + "] \033[36m" + className + "\033[38m : " + msg + "\n";
}
});
logger.addHandler(handler);
logger.info("我是测试消息1...");
logger.log(Level.INFO, "我是测试消息2...");
logger.log(Level.WARNING, "我是测试消息3...");
}
日志可以设置过滤器,如果我们不希望某些日志信息被输出,我们可以配置过滤规则:
public static void main(String[] args) throws IOException {
Logger logger = Logger.getLogger(Main.class.getName());
//自定义过滤规则
logger.setFilter(record -> !record.getMessage().contains("普通"));
logger.log(Level.SEVERE, "严重的错误", new IOException("我就是错误"));
logger.log(Level.WARNING, "警告的内容");
logger.log(Level.INFO, "普通的信息");
}
Properties配置文件
Properties文件:
name=Test
desc=Description
该文件配置很简单,格式为配置项=配置值
,我们可以直接通过Properties
类来将其读取为一个类似于Map一样的对象:
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
properties.load(new FileInputStream("test.properties"));
System.out.println(properties);
}
Properties
类是继承自Hashtable
,而Hashtable
是实现的Map接口,也就是说,Properties
本质上就是一个Map一样的结构,它会把所有的配置项映射为一个Map
需要注意的是因为Properties
类是继承自Hashtable
,hashtable
中key
和value
的值都不能为null
当然它也可以像一般的Map对象一样使用put
、set
以及get
方法
使用store
方法可以将该Properties
对象放入输出流保存
//保存到文件“test.txt”中,第二个参数为输出在`Properties`对象数据前的内容
properties.store(new FileOutputStream("test.txt"),"test");
//打印到控制台
properties.store(System.out,"test");
编写日志配置文件
# RootLogger 的默认处理器为
handlers= java.util.logging.ConsoleHandler
# RootLogger 的默认的日志级别
.level= CONFIG
使用配置文件进行配置
public static void main(String[] args) throws IOException {
//获取日志管理器
LogManager logManager = LogManager.getLogManager();
//读取配置文件
logManager.readConfiguration(new FileInputStream("test.properties"));
//获取日志记录器
Logger logger = Logger.getLogger(Main.class.getName());
//输出日志
logger.log(Level.INFO, "Hello World!");
}
需要注意的是控制日志输出的因素有两个,一个是日志本身的级别,也就是第一个配置文件中设置的日志级别,还有一个是日志处理器Handler
的级别
在第一个配置文件中我们设置的级别是CONFIG
,但是实际上我们是无法输出CONFIG
级别的日志的,即便是设置成ALL
也不行,因为当前logger使用的日志处理器默认等级是INFO
,所以INFO
以下等级的日志我们都无法输出,如果想要输出其他等级的日志信息,必须修改ConsoleHandler
的默认配置:
# 指定默认日志级别
java.util.logging.ConsoleHandler.level = ALL
# 指定默认日志消息格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 指定默认的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
使用Lombok快速开启日志
Logger可以使用Lombok快速生成的。
@Log
public class Main {
public static void main(String[] args) {
System.out.println("自动生成的Logger名称:"+log.getName());
log.info("我是日志信息");
}
}
只需要添加一个@Log
注解即可,添加后,我们可以直接使用一个静态变量log,而它就是自动生成的Logger。我们也可以手动指定名称:
@Log(topic = "打工是不可能打工的")
public class Main {
public static void main(String[] args) {
System.out.println("自动生成的Logger名称:"+log.getName());
log.info("我是日志信息");
}
}
同样需要注意,该logger的日志等级为INFO
,使用的日志处理器日志等级也为INFO
,如果需要输出其他等级的日志,需要修改log 的等级和ConsoleHandler
的默认配置
Mybatis日志系统
开启Mybatis的日志系统,需要在Mybatis的配置文件中添加
<setting name="logImpl" value="STDOUT_LOGGING" />
logImpl
包括很多种配置项,包括 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING,而默认情况下是未配置,也就是说不打印。
使用Java内置的日志功能(JUL)
<setting name="logImpl" value="JDK_LOGGING" />
因为Mybatis的日志级别都比较低,因此我们需要设置一下logging.properties
默认的日志级别:
handlers= java.util.logging.ConsoleHandler
.level= ALL
java.util.logging.ConsoleHandler.level = ALL
修改日志的打印格式
新建一个格式化类继承Formatter,Formatter是一个抽象类,必须实现其抽象方法format
public class TestFormatter extends Formatter {
@Override
public String format(LogRecord record) {
SimpleDateFormat format= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
String time = format.format(new Date(record.getMillis())); //格式化时间
return time + " : " + record.getMessage() + "\n";
}
}
再来修改一下默认的格式化实现:
handlers= java.util.logging.ConsoleHandler
.level= ALL
java.util.logging.ConsoleHandler.level= ALL
java.util.logging.ConsoleHandler.formatter = com.test.TestFormatter