Java日志系列——概述,JUL
Java日志系列——概述,JUL
- 日志
- 概述
- 日志的作用
- 常用日志框架
- JUL
- 组件
- 级别(从高到低)
- QuickStart(控制台打印日志)
- 解释
- 修改打印日志的级别
- 简单设置日志级别
- 使用`Level.ALL`打印所有日志级别
- 打印所有级别(解决)
- 核心
- 完整代码
- 了解JUL的Handler
- ConsoleHandler
- FileHandler
- MemoryHandler
- SocketHandler
- SLF4JBridgeHandler
- 向文件中输出日志
- Logger之间的父子关系
- 自定义继承关系
- 去除继承关系
- JUL日志格式化
- 方法
- 实例
- 自定义配置文件
- 如何读取配置文件
- 1.获取logManager对象
- 2.读取配置
- QuickStart
- 1.在resource下放置配置文件
- 2.设置读取配置文件
- 3.输入日志
- 关于配置文件
- 指定设置单独的handler
- Filter过滤器
- 打印异常堆栈
日志
概述
日志文件是用于记录系统操作事件的文件集合,可分为事件日志和消息日志。具有处理历史数据、诊断问题的追踪以及理解系统的活动等重要作用。
在计算机中,日志文件是记录在操作系统或其他软件运行中发生的事件或在通信软件的不同用户之间的消息的文件。记录是保持日志的行为。在最简单的情况下,消息被写入单个日志文件。
日志的作用
- 调试
- 错误定位
- 数据分析
常用日志框架
- JUL
- logback
- log4j
- log4j2
JUL
JUL全称ava util Logging是java原生的日志框架,使用时不需要另外引用第三方类库,相对其他日志框架使用方便,学习简单,能够在小型应用中灵活使用。
组件
- Loggers:被称为记录器,应用程序通过获取Logger对象,调用其API来来发布日志信息。Logger通常时应用程序访问日志系统的入口程序。
- Appenders:也被称为Handlers,每个Logger都会关联一组Handlers,Logger会将日志交给关联Handlers处理,由Handlers负责将日志做记录。Handlers在此是一个抽象,其具体的实现决定了日志记录的位置可以是控制台、文件、网络上的其他日志服务或操作系统日志等。
- Layouts:也被称为Formatters,它负责对日志事件中的数据进行转换和格式化。Layouts决定了数据在一条日志记录中的最终形式。
- Level:每条日志消息都有一个关联的日志级别。该级别粗略指导了日志消息的重要性和紧迫,我可以将Level和Loggers,Appenders做关联以便于我们过滤消息。
- Filters:过滤器,根据需要定制哪些信息会被记录,哪些信息会被放过。
级别(从高到低)
- severe
- warning
- info
- config
- fine
- finer
- finest
QuickStart(控制台打印日志)
@Test
void contextLoads() {
//获取logger对象
final Logger test1 = Logger.getLogger("test1");
//严重级别日志(最高级别)
test1.severe("最高严重级别的日志输出");
//警告级别日志
test1.warning("警告类型的日志输出");
//信息级别日志(默认级别)
test1.info("信息级别日志输出");
//config,配置级别日志
test1.config("配置级别日志输出");
//fine(没有问题的日志)
test1.fine("没啥问题");
//finer(比没有问题更没问题)
test1.finer("更加没啥问题了!");
//finest(最低级别)
test1.finest("超级ok");
}
解释
从quickstart中我们发现所有打印的级别中除了前三项以外都没有打印,也就是说从config开始的级别日志对于重要程度上比较低,所以不需要进行打印,这也是默认的配置,我们可用对默认配置进行修改
在jdk9及以上日志输出级别的配置文件在conf/logging.properties
修改打印日志的级别
简单设置日志级别
调用setLevel
方法
final Logger test1 = Logger.getLogger("test1");
test1.setLevel(Level.FINEST);
这里我们将级别设置为FINEST
但是实际上也只会打印到CONFIG
为止,后续从FINE
开始的都不会被打印
使用Level.ALL
打印所有日志级别
即使我们使用.setLevel(Level.ALL)
也仅能打印到CONFIG
级别为止,和上面的结果是一样的!
那如何才能真正打印所有的日志级别呢(请看下面)
打印所有级别(解决)
核心
我们不仅要设置原始的Logger
对象的日志级别,我们还要让Logger
添加处理器(Handler)以及日志格式化器(Formatter)
//获取logger对象
final Logger test1 = Logger.getLogger("test1");
//添加handler
final ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setFormatter(new SimpleFormatter());
test1.addHandler(consoleHandler);
consoleHandler.setLevel(Level.ALL);
//修改日志级别
test1.setLevel(Level.ALL);
这里我们添加了SimpleFormatter
作为我们的Formatter,添加了ConsoleHandler
作为Handler,这也是JUL的默认设置的,经过我们查询就能发现
再设置完Handler之后我们还需要设置Handler的输出级别,因为真实处理时Handler进行处理的所以我们必须这样才能保证设置起效
完整代码
@Test
void contextLoads() {
//获取logger对象
final Logger test1 = Logger.getLogger("test1");
//添加handler
final ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setFormatter(new SimpleFormatter());
test1.addHandler(consoleHandler);
consoleHandler.setLevel(Level.ALL);
//修改日志级别
test1.setLevel(Level.ALL);
//严重级别日志(最高级别)
test1.severe("最高严重级别的日志输出");
//警告级别日志
test1.warning("警告类型的日志输出");
//信息级别日志(默认级别)
test1.info("信息级别日志输出");
//config,配置级别日志
test1.config("配置级别日志输出");
//fine(没有问题的日志)
test1.fine("没啥问题");
//finer(比没有问题更没问题)
test1.finer("更加没啥问题了!");
//finest(最低级别)
test1.finest("超级ok");
}
了解JUL的Handler
通过查询我们得到
- ConsoleHandler:控制台输出处理器
- FileHandler:文件处理器
- MemoryHandler:内存处理器
- SocketHandler:网络socket处理器
- SLF4JBridgeHandler:slf4j的桥梁转化处理器
ConsoleHandler
此Handler将日志记录发布到System.err 。默认情况下, SimpleFormatter用于生成简短摘要
FileHandler
简单的文件记录Handler程序。
FileHandler可以写入指定的文件,也可以写入旋转的文件集
MemoryHandler
在内存中的循环缓冲区中缓冲请求的Handler程序。
通常,此Handler只是将传入的LogRecords存储到其内存缓冲区中并丢弃早期的记录。这种缓冲非常方便并且避免了格式化成本。在某些触发条件下, MemoryHandler会将其当前缓冲区内容推送到目标Handler ,该目标通常会将它们发布到外部
SocketHandler
简单的网络日志Handler 。
LogRecords发布到网络流连接。默认情况下, XMLFormatter类用于格式化。
SLF4JBridgeHandler
将所有 JUL 日志记录桥接/路由到 SLF4J API。
本质上,这个想法是在根记录器上安装一个SLF4JBridgeHandler的实例作为系统中唯一的 JUL 处理程序。随后,SLF4JBridgeHandler 实例会将所有 JUL 日志记录重定向到基于以下级别映射的 SLF4J API:
- FINEST -> TRACE
- FINER -> DEBUG
- FINE -> DEBUG
- INFO -> INFO
- WARNING -> WARN
- SEVERE -> ERROR
向文件中输出日志
初始化一个FileHandler以写入给定的文件名,并带有可选的附加。
FileHandler是基于LogManager属性(或其默认值)配置的,可以写入的数据量没有限制,因此请谨慎使用。(写入过多日志会导致文件过大)
@Test
void test() throws IOException {
final Logger test2 = Logger.getLogger("test2");
/**
* 参数
* pattern - 输出文件的名称
* append – 指定附加模式
*/
final FileHandler fileHandler = new FileHandler("D:\\log_test.log", true);
fileHandler.setFormatter(new SimpleFormatter());
fileHandler.setLevel(Level.ALL);
test2.setLevel(Level.ALL);
test2.addHandler(fileHandler);
test2.severe("严重错误");
test2.info("信息级别日志输出");
test2.config("配置级别日志输出");
test2.fine("没啥问题");
}
Logger之间的父子关系
JUL中Logger之间存在父子关系,这种父子关系通过树状结构存储,JUL在初始化时会创建一个顶层RootLogger作为所有Logger父Logger,存储上作为树状结构的根节点。并父子关系通过名称来关联。默认子Logger会继承父Logger的属性。
自定义继承关系
自定义的继承关系取决于你的名称的层级关系
@Test
void test2(){
final Logger blog = Logger.getLogger("a.b");
final Logger clog = Logger.getLogger("a.b.c");
final Logger parent = clog.getParent();
System.out.println(parent.getName());
}
去除继承关系
我们使用.setUseParentHandlers(false);
进行设置即可去除继承关系进行自定义设置
JUL日志格式化
我们可用通过实现Formatter自定义日志格式化的方式
做法很简单,实现Formatter接口,然后模仿SimpleFormatter
写就可以了,当然我们也可以使用内部类的方式来进行实现
方法
- record.getLevel():返回日志级别
- record.getInstant() :返回时间戳(日期)
- record.getLoggerName() :返回日志名称
- record.getMessage() :返回日志信息
- record.getMillis() :返回时间戳(毫秒)
- record.getParameters() :返回日志传参
- record.getSourceClassName() :返回日志所在类
- record.getSourceMethodName():返回日志所在方法
实例
@Test
void test2() {
Logger clog = Logger.getLogger("a.b.c");
ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setFormatter(new DefineFormatter());
consoleHandler.setLevel(Level.ALL);
clog.setLevel(Level.ALL);
clog.setUseParentHandlers(false);
clog.addHandler(consoleHandler);
clog.severe("严重的!!");
clog.info("信息。。。。");
}
自定义配置文件
如何读取配置文件
1.获取logManager对象
final LogManager logManager = LogManager.getLogManager();
2.读取配置
logManager.readConfiguration(Thread.currentThread().getContextClassLoader().getResourceAsStream("文件名"))
QuickStart
1.在resource下放置配置文件
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.maxLocks = 100
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
com.xyz.foo.level = SEVERE
2.设置读取配置文件
final LogManager logManager = LogManager.getLogManager();
//读取配置文件
logManager.readConfiguration(Thread.currentThread().getContextClassLoader().getResourceAsStream("logging.properties"));
3.输入日志
@Test
void test3() throws IOException {
final LogManager logManager = LogManager.getLogManager();
//读取配置文件
logManager.readConfiguration(Thread.currentThread().getContextClassLoader().getResourceAsStream("logging.properties"));
final Logger test4 = Logger.getLogger("test4");
test4.fine("fine .....");
}
关于配置文件
- handlers:用于指定rootLogger处理器
- java.util.logging.FileHandler.pattern:指定文件处理器的位置
- java.util.logging.FileHandler.limit :单文件大小限制
- java.util.logging.FileHandler.count :指定能够报错多少个日志文件
- java.util.logging.FileHandler.maxLocks :指定锁的数量
- java.util.logging.FileHandler.formatter :制定格式化器
- java.util.logging.ConsoleHandler.level :指定输出级别
- java.util.logging.FileHandler.append:指定是否追加写(true表示追加写)
指定设置单独的handler
例如我们要指定a.b.c
的handler为ConsoleHandler
a.b.c.handler:java.util.logging.ConsoleHandler
当然其他单独的配置也是这样!
Filter过滤器
设置过滤器使用setFilter
方法进行设置,我们需要自己进行定义实现
.setFilter(record -> {});
我们通过对LogRecord 对象用于在日志框架和各个日志处理程序之间传递日志请求。
进行自定义实现,通常来说我们会使用Filter设置个性化的实现例如设置在哪段时间内输出日志,而其他时间不输出
打印异常堆栈
对于报错信息我们应该将其异常打印出来
需要使用throwing
方法进行捕获抛出
需要注意的是我们需要将级别设置为Finer!
logger.throwing("logger名称","方法名",error);