日志文件是用于记录系统操作事件的文件集合,可分为事件日志和消息日志。具有处理历史数据、诊断问题的追踪以及理解系统的活动等重要作用。
在计算机中,日志文件是记录在操作系统或其他软件运行中发生的事件或在通信软件的不同用户之间的消息的文件。记录是保持日志的行为。在最简单的情况下,消息被写入单个日志文件。
JUL全称ava util Logging是java原生的日志框架,使用时不需要另外引用第三方类库,相对其他日志框架使用方便,学习简单,能够在小型应用中灵活使用。
@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");
}

通过查询我们得到

此Handler将日志记录发布到System.err 。默认情况下, SimpleFormatter用于生成简短摘要
简单的文件记录Handler程序。
FileHandler可以写入指定的文件,也可以写入旋转的文件集
在内存中的循环缓冲区中缓冲请求的Handler程序。
通常,此Handler只是将传入的LogRecords存储到其内存缓冲区中并丢弃早期的记录。这种缓冲非常方便并且避免了格式化成本。在某些触发条件下, MemoryHandler会将其当前缓冲区内容推送到目标Handler ,该目标通常会将它们发布到外部
简单的网络日志Handler 。
LogRecords发布到网络流连接。默认情况下, XMLFormatter类用于格式化。
将所有 JUL 日志记录桥接/路由到 SLF4J API。
本质上,这个想法是在根记录器上安装一个SLF4JBridgeHandler的实例作为系统中唯一的 JUL 处理程序。随后,SLF4JBridgeHandler 实例会将所有 JUL 日志记录重定向到基于以下级别映射的 SLF4J API:
初始化一个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("没啥问题");
}

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);进行设置即可去除继承关系进行自定义设置
我们可用通过实现Formatter自定义日志格式化的方式
做法很简单,实现Formatter接口,然后模仿SimpleFormatter写就可以了,当然我们也可以使用内部类的方式来进行实现
@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("信息。。。。");
}

final LogManager logManager = LogManager.getLogManager();
logManager.readConfiguration(Thread.currentThread().getContextClassLoader().getResourceAsStream("文件名"))

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
final LogManager logManager = LogManager.getLogManager();
//读取配置文件
logManager.readConfiguration(Thread.currentThread().getContextClassLoader().getResourceAsStream("logging.properties"));
@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 .....");
}

例如我们要指定a.b.c的handler为ConsoleHandler
a.b.c.handler:java.util.logging.ConsoleHandler
当然其他单独的配置也是这样!
设置过滤器使用setFilter方法进行设置,我们需要自己进行定义实现
.setFilter(record -> {});
我们通过对LogRecord 对象用于在日志框架和各个日志处理程序之间传递日志请求。 进行自定义实现,通常来说我们会使用Filter设置个性化的实现例如设置在哪段时间内输出日志,而其他时间不输出
对于报错信息我们应该将其异常打印出来
需要使用throwing方法进行捕获抛出
需要注意的是我们需要将级别设置为Finer!
logger.throwing("logger名称","方法名",error);