调试日志在开发过程中调试程序,输出的日志。
系统日志是用来记录系统中硬件、软件和系统相关问题的信息。同时还可以监视系统中发生的事件。用户可以通过它来检查错误发生的原因,或者寻找收到攻击时留下的痕迹。系统日志包括系统日志、应用日志和安全日志这几种分类。
(1)控制日志输出的内容和格式。
(2)控制日志输出的位置。
(3)日志文件相关的优化,如异步、归档、压缩。
(4)日志系统的维护。
(5)面向接口开发–日志的门面。
JUL java util logging: Java原生日志框架
Log4j: Apache的一个开源项目
Logback: 由Log4j之父做的另一个开源项目,一个可靠、通用且灵活的java日志框架
Log4j2: Log4j官方的第二个版本,各个方面都是与Logback及其相似。具有插件式结构、配置文件优化等特征,Spring Boot1.4 版本以后就不再支持log4j,所以第二个版本应运而生
日志门面技术 JCL、SLF4j
4.1 降低代码和日志框架的耦合性:日志门面技术需要配合日志框架使用,并且可以做到在不改动代码的情况下,可以切换日志框架。
抽取出方法c,其实内部调用的还是各自记录日志的方法。
JUL是Java自带的框架,使用时不需要另外引用第三方的类库,相对其他框架使用简单,主要是使用在小型应用中,我看公司的代码比较老的项目用的是这个框架。
Logger:被称为记录器,应用程序通过获取Logger对象,调用其API来发布日志信息。Logger通常被认为是访问日志系统的入口程序。
Handler:处理器,每个Logger都会关联一个或者是一组Handler,Logger会将日志交给关联的Handler去做处理,由Handler负责将日志做记录。Handler具体实现了日志的输出位置,比如可以输出到控制台或者文件中等等。
Filter:过滤器,根据需要定制哪些信息会被记录,哪些信息会被略过。
Formatter:格式化组件,它负责对日志中的数据和信息进行转换和格式化,所以它决定了我们日志输出最终的形式。
Level:日志的输出级别,每条日志消息都有一个关联的级别。我们根据输出级别的设置,用来展现最终所呈现的日志信息。
1)Logger对象的创建方式:不能直接new 对象,用getLogger方法获取logger对象。
//com.form.project.test.LogDemoCtroller为当前类的全路径
Logger logger = Logger.getLogger("com.form.project.test.LogDemoCtroller");
2)日志的两种输出方式:
//方式一:直接调用日志级别相关的方法,方法中传递日志输出信息
logger.info("输出info信息1");
//方式二:调用通用的log方法,然后在里面通过level类型来定义日志的级别参数,以及搭配日志输出信息的参数。
logger.log(Level.INFO,"输出info信息2");
3)日志中的占位符
/**
* 我们可以使用字符串拼接的方式输出学生信息 姓名 年龄
*/
String name = "张三";
int age = 23;
logger.log(Level.INFO,"学生的姓名为:"+name+";年龄为:"+age);
但是字符串拼接的方式程序效率低,可读性不强,维护成本高,所以我们可以使用占位符的方式来进行操作。
//0表示是第一个占位符 1表示第二个占位符
String name = "张三";
int age = 23;
logger.log(Level.INFO,"学生的姓名:{0},年龄:{1}",new Object[]{name,age});
1)JUL日志的级别
SEVERE:(最高级)错误 ----最高级的日志级别
WARNING:警告
INFO:(默认级别)消息
CONFIG:配置
FINE:详细信息(少)
FINER:详细信息(中)
FINEST:详细信息(多)----最低级的日志级别
两个特殊的级别
OFF: 可用来关闭日志记录
ALL: 启用所有消息的日志记录
/**
* 我们可以看到第二个参数是数值,数值的意义在于,如果我们设置的日志的级别是INFO--800
* 那么最终展现的日志信息,必须是数值大于800的所有日志信息
* 即打印出的级别为:INFO、WARNING、SEVERE
**/
//部分日志级别源码:
public static final Level SEVERE = new Level("SEVERE",1000, defaultBundle);
public static final Level WARNING = new Level("WARNING", 900, defaultBundle);
public static final Level INFO = new Level("INFO", 800, defaultBundle);
public static final Level CONFIG = new Level("CONFIG", 700, defaultBundle);
public static final Level FINE = new Level("FINE", 500, defaultBundle);
public static final Level FINER = new Level("FINER", 400, defaultBundle);
public static final Level FINEST = new Level("FINEST", 300, defaultBundle);
2)默认的日志级别
默认的日志级别为info,我们下面代码程序中有7个日志级别,但是输出只有3个日志级别,说明默认的日志级别是info,这样就是输出了info及以上的信息。
Logger logger = Logger.getLogger("com.form.project.test.LogDemoCtroller");
logger.severe("severe信息");
logger.warning("warning信息");
logger.info("info信息");
logger.config("config信息");
logger.fine("fine信息");
logger.finer("finer信息");
logger.finest("finest信息");
输出:
九月 17, 2022 5:30:32 下午 com.form.project.test.LogDemoCtroller test02
严重: severe信息
九月 17, 2022 5:30:32 下午 com.form.project.test.LogDemoCtroller test02
警告: warning信息
九月 17, 2022 5:30:32 下午 com.form.project.test.LogDemoCtroller test02
信息: info信息
3)自定义日志级别
Logger.getLogger("com.form.project.test.LogDemoCtroller");
//将默认的日志打印方式关闭掉
//参数设置为flase,我们打印日志的方式就不会按照父logger默认的方式进行操作
logger.setUseParentHandlers(false);
//处理器Handler
//在此我们使用的是控制台日志处理器,取得处理器对象
ConsoleHandler handler = new ConsoleHandler();
//创建日志格式化组件对象
SimpleFormatter formatter = new SimpleFormatter();
//在处理器中设置输出格式
handler.setFormatter(formatter);
//在记录器中添加处理器
logger.addHandler(handler);
//设置日志的打印级别
//此处必须将日志记录器和处理器的级别进行统一的设置,才会达到日志显示相应级别的效果
logger.setLevel(Level.ALL);
handler.setLevel(Level.ALL);
logger.severe("severe信息");
logger.warning("warning信息");
logger.info("info信息");
logger.config("config信息");
logger.fine("fine信息");
logger.finer("finer信息");
logger.finest("finest信息");
输出:
九月 17, 2022 8:25:50 下午 com.form.project.test.LogDemoCtroller test02
严重: severe信息
九月 17, 2022 8:25:50 下午 com.form.project.test.LogDemoCtroller test02
警告: warning信息
九月 17, 2022 8:25:50 下午 com.form.project.test.LogDemoCtroller test02
信息: info信息
九月 17, 2022 8:25:50 下午 com.form.project.test.LogDemoCtroller test02
配置: config信息
九月 17, 2022 8:25:50 下午 com.form.project.test.LogDemoCtroller test02
详细: fine信息
九月 17, 2022 8:25:50 下午 com.form.project.test.LogDemoCtroller test02
较详细: finer信息
九月 17, 2022 8:25:50 下午 com.form.project.test.LogDemoCtroller test02
非常详细: finest信息
1)JUL文件日志的打印
@Test
public void test03() throws IOException {
/*
将日志输出到具体的磁盘文件中
这样做相当于是做了日志的持久化操作
*/
Logger logger = Logger.getLogger("com.form.project.test.LogDemoCtroller");
logger.setUseParentHandlers(false);
//文件日志处理器
FileHandler fileHandler = new FileHandler("/Users/chezhewei/Desktop/mylog.log");
//创建日志格式化组件对象
SimpleFormatter formatter = new SimpleFormatter();
//在处理器中设置输出格式
fileHandler.setFormatter(formatter);
//在记录器中添加处理器
logger.addHandler(fileHandler);
logger.severe("severe信息");
logger.warning("warning信息");
logger.info("info信息");
logger.config("config信息");
logger.fine("fine信息");
logger.finer("finer信息");
logger.finest("finest信息");
}
在桌面生成了log日志文件,如图所示
文件中的内容为:
九月 17, 2022 8:50:03 下午 com.form.project.test.LogDemoCtroller test03
严重: severe信息
九月 17, 2022 8:50:03 下午 com.form.project.test.LogDemoCtroller test03
警告: warning信息
九月 17, 2022 8:50:03 下午 com.form.project.test.LogDemoCtroller test03
信息: info信息
日志的打印只是将控制台处理器变成了文件处理器,也可以同时设置控制台处理和文件处理,也就是同时添加多个处理器。
public void test03() throws IOException {
/*
将日志输出到具体的磁盘文件中
这样做相当于是做了日志的持久化操作
*/
Logger logger = Logger.getLogger("com.form.project.test.LogDemoCtroller");
logger.setUseParentHandlers(false);
//文件日志处理器
FileHandler fileHandler = new FileHandler("/Users/chezhewei/Desktop/mylog.log");
//创建日志格式化组件对象
SimpleFormatter formatter = new SimpleFormatter();
//在处理器中设置输出格式
fileHandler.setFormatter(formatter);
//在记录器中添加处理器
logger.addHandler(fileHandler);
//同时设置在控制台和文件中进行打印
ConsoleHandler handler = new ConsoleHandler();
//在处理器中设置输出格式
handler.setFormatter(formatter);
//在记录器中添加处理器
logger.addHandler(handler);
logger.severe("severe信息");
logger.warning("warning信息");
logger.info("info信息");
logger.config("config信息");
logger.fine("fine信息");
logger.finer("finer信息");
logger.finest("finest信息");
}
Logger之间的"父子"关系,这种父子关系不是继承关系,是通过树状结构存储的(代码包的层级)。
RootLogger是所有logger对象的顶层logger,logger1的父类也为RootLogger。
JUL在初始化时,会创建一个顶层RootLogger作为所有Logger的父Logger
查看源码:
owner.rootLogger = owner.new RootLogger();
rootLogger是LogManager的内部类
java.util.logging.LogManager$RootLogger默认名称为 空串
父子关系同时也是节点之间的挂载关系
LoggerContext cx = getUserContext();//LoggerContext 一种用来保存节点的Map关系
private LogNode node;//节点
Logger logger1 = Logger.getLogger("com.form.project.test");//父(父logger的父亲是RootLogger)
Logger logger2 = Logger.getLogger("com.form.project.test.LogDemoCtroller");//子
//System.out.println(logger2.getParent() == logger1);//输出为true
System.out.println("logger1的父logger引用为:"+logger1.getParent()+";父亲的名称为"+logger1.getParent().getName());
System.out.println("logger2的父logger引用为:"+logger2.getParent()+";父亲的名称为"+logger2.getParent().getName());
输出为:
logger1的父logger引用为:java.util.logging.LogManager$RootLogger@1c72da34;父亲的名称为
logger2的父logger引用为:java.util.logging.Logger@6b0c2d26;父亲的名称为com.tang.project1.test
/*
父亲所做的设置,也能够同时作用于儿子
对logger1做的日志打印相关的设置,然后我们使用logger2进行日志的打印
*/
//父类做输出
logger1.setUseParentHandlers(false);
ConsoleHandler handler = new ConsoleHandler();
SimpleFormatter formatter = new SimpleFormatter();
handler.setFormatter(formatter);
logger1.addHandler(handler);
logger1.setLevel(Level.ALL);
handler.setLevel(Level.ALL);
//子类做打印
logger2.severe("severe信息");
logger2.warning("warning信息");
logger2.info("info信息");
logger2.config("config信息");
logger2.fine("fine信息");
logger2.finer("finer信息");
logger2.finest("finest信息");
输出如下:
九月 17, 2022 9:46:04 下午 com.form.project.test.LogDemoCtroller test04
严重: severe信息
九月 17, 2022 9:46:04 下午 com.form.project.test.LogDemoCtroller test04
警告: warning信息
九月 17, 2022 9:46:04 下午 com.form.project.test.LogDemoCtroller test04
信息: info信息
九月 17, 2022 9:46:04 下午 com.form.project.test.LogDemoCtroller test04
配置: config信息
九月 17, 2022 9:46:04 下午 com.form.project.test.LogDemoCtroller test04
详细: fine信息
九月 17, 2022 9:46:04 下午 com.form.project.test.LogDemoCtroller test04
较详细: finer信息
九月 17, 2022 9:46:04 下午 com.form.project.test.LogDemoCtroller test04
非常详细: finest信息
1)系统默认配置文件
以上所有的配置和相关的操作,都是以java硬编码的形式进行的我们可以使用更加清晰,更加专业的一种做法,就是使用配置文件。如果我们没有自己添加配置文件,则会使用系统默认的配置文件。
系统默认的配置文件在:jre–>lib–>logging.properties
默认配置文件信息解释:
2)使用自定义配置文件
使用自定义配置文件:
@Test
public void test06() throws IOException {
InputStream inputStream = new FileInputStream("自定义配置文件地址");
//取得日志管理器对象
LogManager logManager = LogManager.getLogManager();
//读取自定义配置文件
logManager.readConfiguration(inputStream);
Logger logger1 = Logger.getLogger("com.form.project.test.LogDemoCtroller");
logger1.severe("severe信息");
logger1.warning("warning信息");
logger1.info("info信息");
logger1.config("config信息");
logger1.fine("fine信息");
logger1.finer("finer信息");
logger1.finest("finest信息");
}
自定义配置文件的输出:
追加日志:
1) 初始化LogManager,LogManager加载logging.properties配置文件,添加Logger到LogManager
2)从单例的LogManager获取Logger
3)设置日志级别level.在打印的过程中使用到了日志记录的LogRecord类
4)Filter作为过滤器提供了日志级别之外更细粒度的控制。
5)Handler日志处理器。决定日志的输出位置.例如控制台、文件。。。
6)Formatter是用来格式化输出的