日志框架 JUL

微信图片_20220316194438

日志框架

日志文件是用于记录系统操作事件的文件集合,可分为事件日志和消息日志。具有处理历史数据、诊断问题的追踪以及理解系统的活动等重要作用

在计算机中,日志文件是记录在操作系统或其他软件运行中发生的事件或在通信软件的不同用户之间的消息的文件。记录是保持日志的行为。在最简单的情况下,消息被写入单个日志文件

日志的作用

  • 调试

在Java项目调试时,查看栈信息可以方便地知道当前程序的运行状态,输出的日志便于记录程序在之前的运行结果。如果你大量使用System.out或者System.err,这是一种最方便最有效的方法,但显得不够专业

  • 错误定位

不要以为项目能正确跑起来就可以高枕无忧,项目在运行一段时候后,可能由于数据问题,网络问题,内存问题等出现异常。这时日志可以帮助开发或者运维人员快速定位错误位置,提出解决方案

  • 数据分析

大数据的兴起,使得大量的日志分析成为可能,ELK也让日志分析门槛降低了很多。日志中蕴含了大量的用户数据,包括点击行为,兴趣偏好等,用户画像对于公司下一步的战略方向有一定指引作用

最常见的普通日志

1
System.out.println();
1
System.err.println();

主流的日志框架

日志实现(具体干活的):JUL(java util logging)、logback、log4j、log4j2

日志门面(指定规则的):JCL(Jakarta Commons Logging)、slf4j( Simple Logging Facade for Java)

JUL 日志框架

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

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

日志的级别

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

  • java.util.logging.Level中定义了日志的级别

server 最高值

warning

info 默认级别

config

fine

finer

finest 最低值

OFF 可用来关闭日志记录

ALL 启用所有消息的日志记录

Logger之间的父子关系

JUL的Logger的父子关系

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

常用的转换符

转换符 详细说明
%s 字符串类型
%c 字符类型
%b 布尔类型
%d(十进制) 整数类型
%x(十六进制) 整数类型(十六进制)
%o 整数类型(八进制)
%f 浮点类型
%a 十六进制浮点类型
%e 指数类型
%n 换行符
%tx 日期与时间类型(x代表不同的日期与时间转换符)

特殊符号

标志 说明 示例 结果
+ 为正数或者负数添加符号,因为一般整数不会主动加符号 (“%+d”,15) +15
0 数字前面补0,用于对齐 (“%04d”, 99) 0099
空格 在整数之前添加指定数量的空格 (“%4d”, 99) 99
, 以“,”对数字分组(常用显示金额) (“%,f”, 9999.99) 9,999.990000
( 使用括号包含负数 (“%(f”, -99.99) (99.990000)

日期处理

标志 说明 示例
c 包括全部日期和时间信息 周四 10月 21 14:52:10 GMT+08:00 2021
F 年-月-日”格式 2021-10-21
D 月/日/年”格式 10/21/21
r HH:MM:SS PM”格式(12时制) 02:53:20 下午
T HH:MM:SS”格式(24时制) 14:53:39
R HH:MM”格式(24时制) 14:53
b 月份本地化 0月
y 两位的年 21
Y 四位的年 2021
m 10
d 21
H 24小时制的时 14
I 12小时制的时 2
M 57
S 46
s 秒为单位的时间戳 1634799527
p 上午还是下午 下午

JUL配置

  • JULTest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
public class JULTest {

static{
//类加载时读取配置文件
LogManager logManager = LogManager.getLogManager();

//寻找配置文件
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("logger.properties");

try {
// 将配置文件流添加至logManager中
logManager.readConfiguration(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}

public static final Logger LOGGER = Logger.getLogger(JULTest.class.getName());

@Test
public void testJuc() throws IOException {

//关闭默认配置
LOGGER.setUseParentHandlers(false);

//手动添加自己的Handler ConsoleHandler控制台处理器
ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setFormatter(new SimpleFormatter());
//将Handler的级别设置为全部显示
consoleHandler.setLevel(Level.ALL);

//手动添加自己的Handler FileHandler 文件处理器
FileHandler fileHandler = new FileHandler("D:/logs/JULTest.log",true);
fileHandler.setFormatter(new SimpleFormatter());
fileHandler.setLevel(Level.ALL);

//将配置的Handler添加至LOGGER
LOGGER.addHandler(consoleHandler);

LOGGER.addHandler(fileHandler);

//设置日志显示级别
LOGGER.setLevel(Level.ALL);

LOGGER.severe("server: 严重");

LOGGER.warning("warning: 警告");

LOGGER.info("info: 信息");

LOGGER.fine("fine: JUL无显示 因为Leve 低于 info JDK默认级别配置的是info及info以上");

LOGGER.finer("finer: JUL无显示 因为Leve 低于 info JDK默认级别配置的是info及info以上");

LOGGER.finest("finest: JUL无显示 因为Leve 低于 info JDK默认级别配置的是info及info以上");

}

@Test
public void testParent(){

//任何一个Logger都是单例的 名字相同的只有一个
Logger logger = Logger.getLogger(JULTest.class.getName());

// System.out.println(LOGGER.getName());
// System.out.println(LOGGER == logger);
//
//// Logger parent = logger.getParent();
////
//// System.out.println(parent);

//创建Logger对象
Logger logger1 = Logger.getLogger("com.dream");

//关闭继承的配置
logger1.setUseParentHandlers(false);

//控制台配置
ConsoleHandler consoleHandler = new ConsoleHandler();

//设置格式
consoleHandler.setFormatter(new SimpleFormatter());

//给控制台设置级别
consoleHandler.setLevel(Level.ALL);

///将处理器添加至logger对象
logger1.addHandler(consoleHandler);

//给logger对象设置级别
logger1.setLevel(Level.ALL);

logger.info("info");

logger.fine("fine");

// parent = logger.getParent();

// System.out.println(parent);
// System.out.println(logger1.getParent());
}

@Test
public void readConfig() throws IOException {

Logger logger = Logger.getLogger(JULTest.class.getName());

logger.warning("warning");
logger.finer("finer");
logger.fine("fine");

try{
Integer result = 1 / 0;
}catch (Exception e){
logger.throwing(JULTest.class.getName(),"readConfig",e);
}
}
}
  • Logger.properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#根配置
handlers= java.util.logging.ConsoleHandler
.level= FINE
# 自定义配置
com.dream.xiaobo.handlers = java.util.logging.ConsoleHandler,java.util.logging.FileHandler
com.dream.xiaobo.level = FINER
# 不继承根的配置
com.dream.xiaobo.useParentHandlers = false

#文件日志配置
java.util.logging.FileHandler.pattern = D:/logs/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.append=true
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.maxLocks = 100
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.FileHandler.level = FINER
java.util.logging.ConsoleHandler.level = FINER

java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

Log4j 日志框架

Log4j是Apache下的一款开源的日志框架

简单演示

  • pom
1
2
3
4
5
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.13</version>
</dependency>
  • java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static final Logger LOGGER = Logger.getLogger(Log4jTest.class);

@Test
public void testLog4j(){

//初始化默认配置
BasicConfigurator.configure();

// 日志记录输出
LOGGER.info("hello log4j");
// 日志级别
LOGGER.fatal("fatal"); // 严重错误,一般会造成系统崩溃和终止运行
LOGGER.error("error"); // 错误信息,但不会影响系统运行
LOGGER.warn("warn"); // 警告信息,可能会发生问题
LOGGER.info("info"); // 程序运行信息,数据库的连接、网络、IO操作等
LOGGER.debug("debug"); // 调试信息,一般在开发阶段使用,记录程序的变量、参数等
LOGGER.trace("trace"); // 追踪信息,记录程序的所有流程信息
}

fatal 指出每个严重的错误事件将会导致应用程序的退出。

error 指出虽然发生错误事件,但仍然不影响系统的继续运行。

warn 表明会出现潜在的错误情形。

info 一般和在粗粒度级别上,强调应用程序的运行全程。

debug 一般用于细粒度级别上,对调试应用程序非常有帮助。

trace 是程序追踪,可以用于输出程序运行中的变量,显示执行的流程。

两个特殊的级别:OFF,可用来关闭日志记录。 ALL,启用所有消息的日志记录

一般情况下,我们只使用4个级别,优先级从高到低为 ERROR > WARN > INFO > DEBUG

组件

Log4J 主要由 Loggers (日志记录器)、Appenders(输出端)和 Layout(日志格式化器)组成。其中 Loggers 控制日志的输出级别与日志是否输出;Appenders 指定日志的输出方式(输出到控制台、文件 等);Layout 控制日志信息的输出格式

Log4J 主要由 Loggers (日志记录器)、Appenders(输出端)和 Layout(日志格式化器)组成。其中 Loggers 控制日志的输出级别与日志是否输出;Appenders 指定日志的输出方式(输出到控制台、文件 等);Layout 控制日志信息的输出格式

Loggers

日志记录器:负责收集处理日志记录,Logger的名字大小写敏感,其命名有继承机制:例如:name为com.dream.xiaobo.Log4j的logger会继承 name为com.dream.xiaobo的logger,和JUL一致。

Log4J中有一个特殊的logger叫做“root”,他是所有logger的根,也就意味着其他所有的logger都会直接 或者间接地继承自root。root logger可以用Logger.getRootLogger()方法获取。 JUL也有一个名为.的根

Appenders

Appender和JUL的Handler很像,用来指定日志输出到哪个地方,可以同时指定日志的输出目的地。Log4j 常用的输出目的地 有以下几种:

输出端类型 作用
ConsoleAppender 将日志输出到控制台
FileAppender 将日志输出到文件中
DailyRollingFileAppender 将日志输出到一个日志文件,并且每天输出到一个新的文件
RollingFileAppender 将日志输出到一个日志文件,并且每天输出到一个新的文件
RollingFileAppender 把日志信息保存到数据库中

log4j占位符

%m 输出代码中指定的日志信息

%p 输出日志级别,及 DEBUG、INFO 等

%n 换行符(Windows平台的换行符为 “\n”,Unix 平台为 “\n”)

%r 输出自应用启动到输出该 log 信息耗费的毫秒数

%c 输出打印语句所属的类的全名

%t 输出产生该日志的线程全名

%d 输出服务器当前时间,默认为 ISO8601,也可以指定格式,如:%d{yyyy年MM月dd日HH:mm:ss}

%l 输出日志时间发生的位置,包括类名、线程、及在代码中的行数。如:Test.main(Test.java:10)

%F 输出日志消息产生时所在的文件名称

%L 输出代码中的行号

%% 输出一个 “%” 字符 可以在 % 与字符之间加上修饰符来控制最小宽度、最大宽度和文本的对其方式。如:

%5c 输出category名称,最小宽度是5,category<5,默认的情况下右对齐

%-5c 输出category名称,最小宽度是5,category<5,”-“号指定左对齐,会有空格

%.5c 输出category名称,最大宽度是5,category>5,就会将左边多出的字符截掉,<5不会有空格

%20.30c category名称<20补空格,并且右对齐,>30字符,就从左边交远销出的字符截掉

%-d{yyyy-MM-dd HH:mm:ss} [%t:%r] -[%p] %m%n

打印:日期 [线程:毫秒数] - [日志级别] - 日志信息 换行

log4j代码整理

  • log4jTest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class Log4jTest {

public static final Logger LOGGER = Logger.getLogger(Log4jTest.class);

@Test
public void testLog4j(){

//初始化默认配置
// BasicConfigurator.configure();

//添加源码自带配置
// LOGGER.addAppender(new ConsoleAppender(new PatternLayout("%r [%t] %p %c %x - %m%n")));

// //模拟源码手动添加配置
// Logger rootLogger = Logger.getRootLogger();
//
// //构建ConsoleAppender
// ConsoleAppender consoleAppender = new ConsoleAppender();
//
// //ConsoleAppender 需要一个输出流 System.out输出字节流 PrintWriter输出字符流
// consoleAppender.setWriter(new PrintWriter(System.out));
//
// //需要一个layout
// Layout layout = new PatternLayout("%-d{yyyy-MM-dd HH:mm:ss} [%t:%r] -[%p] %m%n");
//
// //将layout组件添加至consoleAppender
// consoleAppender.setLayout(layout);
//
// //将consoleAppender组件添加至rootLogger
// rootLogger.addAppender(consoleAppender);

// JDBCAppender jdbcAppender = new JDBCAppender();
//
// jdbcAppender.setDriver("com.mysql.cj.jdbc.Driver");
// jdbcAppender.setURL("jdbc:mysql://localhost:3306/test?useUnicode=true&useSSL=false&characterEncoding=utf-8&serverTimezone=UTC");
// jdbcAppender.setUser("root");
// jdbcAppender.setPassword("xiaobo");
// jdbcAppender.setSql("INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('xiaobo','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')");
//
// rootLogger.addAppender(jdbcAppender);

// 日志记录输出
LOGGER.info("hello log4j");
// 日志级别
LOGGER.fatal("fatal"); // 严重错误,一般会造成系统崩溃和终止运行
LOGGER.error("error"); // 错误信息,但不会影响系统运行
LOGGER.warn("warn"); // 警告信息,可能会发生问题
LOGGER.info("info"); // 程序运行信息,数据库的连接、网络、IO操作等
LOGGER.debug("debug"); // 调试信息,一般在开发阶段使用,记录程序的变量、参数等
LOGGER.trace("trace"); // 追踪信息,记录程序的所有流程信息

while (true){
LOGGER.info("hello log4j");
}

}

@Test
public void log4jConfig(){

// 日志记录输出
LOGGER.info("hello log4j");
// 日志级别
LOGGER.fatal("fatal"); // 严重错误,一般会造成系统崩溃和终止运行
LOGGER.error("error"); // 错误信息,但不会影响系统运行
LOGGER.warn("warn"); // 警告信息,可能会发生问题
LOGGER.info("info"); // 程序运行信息,数据库的连接、网络、IO操作等
LOGGER.debug("debug"); // 调试信息,一般在开发阶段使用,记录程序的变量、参数等
LOGGER.trace("trace"); // 追踪信息,记录程序的所有流程信息

}
}
  • log4j.properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=ALL, A1, DailyRolling

# 控制台 Appender
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# 自定义配置
log4j.logger.com.dream.xiaobo =DEBUG, A1, DailyRolling
# 关闭继承根结点的配置
log4j.additivity.com.dream.xiaobo = false

# PatternLayout 布局
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n


# 文件 Appender
log4j.appender.FILE = org.apache.log4j.FileAppender
log4j.appender.FILE.File = D:/logs/log4j.log
log4j.appender.FILE.layout = org.apache.log4j.PatternLayout
log4j.appender.FILE.append = true
log4j.appender.FILE.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

# JDBC Appender
log4j.appender.JDBC = org.apache.log4j.jdbc.JDBCAppender
log4j.appender.JDBC.Driver = com.mysql.cj.jdbc.Driver
log4j.appender.JDBC.URL = jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
log4j.appender.JDBC.User = root
log4j.appender.JDBC.Password = xiaobo
log4j.appender.JDBC.Sql = INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('xiaobo','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
log4j.appender.JDBC.layout = org.apache.log4j.PatternLayout
log4j.appender.JDBC.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n


# 滚动生成文件日志 Appender RollingFileAppender 通过文件大小来控制的
log4j.appender.RollingFile = org.apache.log4j.RollingFileAppender
log4j.appender.RollingFile.File = D:/logs/log4j.log
log4j.appender.RollingFile.append = true
log4j.appender.RollingFile.maximumFileSize = 1
log4j.appender.RollingFile.maxBackupIndex = 1

log4j.appender.RollingFile.layout =org.apache.log4j.PatternLayout
log4j.appender.RollingFile.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n


# 滚动生成文件日志 Appender DailyRollingFileAppender 通过日期来控制的
log4j.appender.DailyRolling = org.apache.log4j.DailyRollingFileAppender
log4j.appender.DailyRolling.File = D:/logs/log4j.log
log4j.appender.DailyRolling.append = true

log4j.appender.DailyRolling.layout =org.apache.log4j.PatternLayout
log4j.appender.DailyRolling.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

本篇文章 仅供自己学习整理 方便阅读 参考b站某UP主 如有侵权 通知即删

你知道的越多 你不知道的越多 嘿 我是小博 带你一起看我目之所及的世界……

-------------本文结束 感谢您的阅读-------------

本文标题:日志框架 JUL

文章作者:小博

发布时间:2022年03月16日 - 19:48

最后更新:2022年03月16日 - 19:49

原始链接:https://codexiaobo.github.io/posts/626247823/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。