日志设置
Logback日志
目录- 1、什么是日志
- 2、Logback简单介绍
- 3、SpringBoot项目中使用logback
- 4、Logback中的一些概念介绍
- 4.1、日志记录器Logger
- 4.2、记录器的层级结构
- 4.3、日志级别
- 4.2.1、java代码演示
- 4.2.2、使用配置文件演示
- 4.4、附加器
- 4.4.1、什么是附加器?
- 4.4.2、附加器的使用
- 4.4.3、输出格式
- 4.4.3.1、输出到控制台
- 4.4.3.2、输出到文件中
- 4.4.3.3、滚动日志
- 按照时间生成文件
- 按照大小和时间生成文件
- 4.4.4、过滤器
- 4.5、记录器的可加性
1、什么是日志
日志的作用是用来追踪和记录我们的程序运行中的信息,我们可以利用日志很快定位问题,追踪分析。
如果没有日志,程序一旦出现问题,很难一下子就能定位问题。尤其是访问第三方接口、随机或偶尔出现的问题、很难再现的问题。
之前听说过一句话:“只有在程序出问题以后才会知道打一个好的日志有多么重要。”
2、Logback简单介绍
目前比较常用的Java日志框架: Logback、log4j、log4j2、JUL等等。
Logback是在log4j 的基础上重新开发的一套日志框架,是完全实现SLF4J接口API(也叫日志门面)。
Logback 的架构非常通用,可以应用于不同的环境。目前logback分为三个模块,logback-core、logback-classic和logback-access。logback-core 模块为其他两个模块奠定了基础。logback-classic模块原生实现了SLF4J API,因此您可以轻松地在 logback 和其他日志记录框架(例如 log4j 1.x 或 java.util.logging (JUL))之间来回切换。
logback-access 模块与 Tomcat 和 Jetty 等 Servlet 容器集成,以提供 HTTP 访问日志功能。请注意,您可以轻松地在 logback-core 之上构建自己的模块。
3、SpringBoot项目中使用logback
logback是springboot-web自带的,无需引入外部依赖即可使用。可以来看下对应的依赖:
4、Logback中的一些概念介绍
4.1、日志记录器Logger
日志记录器(Logger):控制要输出哪些日志记录语句,对日志信息进行级别限制。
如何生成记录器:
final static Logger logger = LoggerFactory.getLogger(“名称”);
这里有两个点需要说明的是:
1、日志记录器是单例的。也就是说第一次发现是没有的,那么创建;第二次直接拿到日志记录器进行使用即可;
2、括号中的无论是字符串还是对应的class类型,最终都会打印成当前的类的全限定类名(正常逻辑)
如下所示:
private static Logger logger = LoggerFactory.getLogger(UserController.class);
private static Logger logger = LoggerFactory.getLogger("com.guang.springbootlogback.user.controller.UserController");
上面二者相同,如果传递的是Class,那么最终也会调用getName来获取得到最终的值。
4.2、记录器的层级结构
类似于java中的体系结构,记录器也是有对应的层级结构的:
根记录器是logback自带的,本身就已经存在。而对于记录器A、B、C来说,根记录器是其父类,对于记录器D来说,是其祖先记录器。
如果对于记录器ABCD都没有设置自身的日志级别的话,那么默认都是继承了根记录器的日志级别。
如果任意一个子记录器设置了自己的日志级别,那么子记录器就可以使用使用对应级别的打印输出内容了。
比如记录器A,继承了根记录器,默认是DEBUG,如果记录器A设置了日志级别为INFO,如果记录器D没有设置的话,将会来继承记录器A的日志级别。
那么分别来创建几个记录器:
private static final Logger logger = LoggerFactory.getLogger("com.guang.logback.demo1.LogbackTestOne");
private static final Logger logger1 = LoggerFactory.getLogger("com.guang.logback");
private static final Logger logger2 = LoggerFactory.getLogger("com.guang");
private static final Logger logger3 = LoggerFactory.getLogger("com");
说明:
1、上面创建了四个记录器,对于logger、logger1、logger2来说,都是继承于logger3的,因为记录器的层级关系是通过包名来进行设置的。
2、对于记录器logger来说,它会创建出来一个具备这层级关系的记录器;
3、对于记录器logger3来说,是继承于根记录器的;
对应图如下所示:
4.3、日志级别
在logback中有五大日志级别,按照从小到大的顺序进行排列:TRACE < DEBUG < INFO < WARN < ERROR
如果设置的级别是DEBUG,那么在程序打印出来的级别只能够大于等于DEBUG的,而不会打印TRACE。
那么logback中默认日志级别是DEBUG
首先需要明确的是对于每个记录器来说,都有日志记录器的名称、级别和获取得到日志级别的方法。
1、name:记录器的名称
2、level:日志记录器的级别,也是日志的级别。五个日志级别:TRACE < DEBUG < INFO < WARN < ERROR
设置的时候可以通过配置文件,也可以通过代码来进行设置。如果没有设置级别,那么将会从父记录器或者是祖先记录器中来进行继承日志级别
3、获取得到级别的方法:
logger.setLevel():设置的级别,只会对这个级别及其之上的起作用
logger.getLevel():获取得到设置的级别
logger.getEffectiveLevel():和getLevel相同
additivity:是否允许叠加打印日志,true或者是false。也就是子记录器和父记录器进行叠加打印,可以理解成是重复打印问题。
后续会来解决这个问题
4.2.1、java代码演示
来利用java代码来设置日志级别,做一个案例演示:
public static void main(String[] args) {
// 不能够使用接口,因为接口中没有这个方法,需要找到具体的实现类
ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(LogbackTestThree.class);
// 在设置之前看下本身的日志级别 为什么?因为直接继承了父记录器都没有设置,那么最终都将会继承根记录器,因为根记录器设置 // 的日志级别是DEBUG,所以默认级别是DEBUG
System.out.println("默认的记录器级别:"+logger.getEffectiveLevel());
// 可以修改父记录器的级别,然后让子记录器来进行继承,子记录器打印的是父记录器的级别
logger.setLevel(Level.INFO);
// 当前级别
logger.info("hello,world");
// 输出更高级别
logger.error("hello,world");
// 输出比当前级别低的 不会来打印
logger.debug("hello,logback---debug");
}
4.2.2、使用配置文件演示
使用java代码来设置日志级别是不方便的,所以最方便的使用方式是使用配置文件来进行输出
这里需要使用到附加器的内容,所以看完附加器再来看这个。
<?xml version="1.0" encoding="UTF-8"?>
C:\\Users\\lig\\Desktop\\log\\out.log
true
log_/date-%d{yyyy-MM-dd HH-mm-ss}-%i.log
30
3MB
500MB
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{60} - %msg%n
UTF-8
4.4、附加器
4.4.1、什么是附加器?
记录器会将输出日志的任务交给附加器完成,不同的附加器会将日志输出到不同的地方,比如控制台附加器、文件附加器、网络附加器等等。
什么样的附加器会将logger.info("输出信息")中的输出信息输出到不同的地方中去
常用的附加器控制台附加器:
- ch.qos.logback.core.ConsoleAppender控制台附加器; 将输出信息输出到控制台;
- ch.qos.logback.core.FileAppender文件附加器; 将输出信息输出到文件中去,文件只有一个;
- ch.qos.logback.core.rolling.RollingFileAppender滚动文件附加器; 将输出信息输出到文件中去,可以输出到不同文件中去;
也就是说将日志输出在哪里?输出到控制台?输出到文件中?(输出到文件中才是生产中的标准)
4.4.2、附加器的使用
附加器就是将信息输出到哪里,但是需要得到日志记录器的支持。所以二者要联合来进行使用。使用方式有两种:
第一种使用方式:记录在当前日志记录器中;
C:\\Users\\lig\\Desktop\\log\\out.log
true
log_/date-%d{yyyy-MM-dd HH-mm-ss}-%i.log
30
3MB
500MB
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{60} - %msg%n
UTF-8
第二种使用方式:记录在根记录器中;(常用)
${logPath}/out.log
${logPath}/date-%d{yyyy-MM-dd}-%i.log
30
500MB
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{60} - %msg%n
UTF-8
4.4.3、输出格式
如果将输出设置的单调,那么将无法来进行定位问题,所以需要对日志产生的时间、位置、日志级别等等信息进行打印,方便我们来定位问题。我们可以指定输出信息的模板,将输出信息进行模板格式化,而Pattern标签就是用来做这个事情的
Pattern由文字文本和转换说明符的格式控制表达式组成 。您可以在其中自由插入任何文字文本。每个转换说明符都以百分号 '%' ,后跟可选的 格式修饰符、转换字和大括号之间的可选参数。转换字控制要转换的数据字段,例如记录器名称、级别、日期或线程名称。格式修饰符控制字段宽度、填充以及左对齐或右对齐。
常见的有如下所示:
下面分别来演示一下使用方式:
4.4.3.1、输出到控制台
<?xml version="1.0" encoding="UTF-8"?>
%10date %level [%thread] %logger{60} %line %msg %n
4.4.3.2、输出到文件中
添加文件附件器
<?xml version="1.0" encoding="UTF-8"?>
%10date %level [%thread] %logger{60} %line %msg %n
mylockbackLog.log
true
%10date %level [%thread] %logger{60} %line %msg %n
但是这个文件附加器有一个问题,就是所有的日志文件都只会输出到这个一个文件中来.随之而来的问题就是时间一旦很长,就会导致文件越来越大,到时候不方便来进行查阅,所以这里需要来进行修改。
我们更加希望的是能够根据时间、文件大小等来对齐进行自定义设置,所以logback也给我们提供了ch.qos.logback.core.rolling.RollingFileAppender滚动文件附加器,可以将我们的日志文件进行归档管理。
4.4.3.3、滚动日志
按照时间生成文件
<?xml version="1.0" encoding="UTF-8"?>
%10date %level [%thread] %logger{60} %line %msg %n
rollback.log
date-%d{yyyy-MM-dd HH-mm/ss}.log
3
50KB
按照大小和时间生成文件
<?xml version="1.0" encoding="UTF-8"?>
%10date %level [%thread] %logger{60} %line %msg %n
rollback.log
date-%d{yyyy-MM-dd HH-mm}-%i.log
3
1KB
5KB
4.4.4、过滤器
过滤器是附加器的一个组件,它用于决定附加器是否输出日志。一个附加器可以包含一个或多个过滤器。每个过滤器都会返回一个枚举值,可选的值:DENY、 NEUTRAL、ACCEPT附加器根据过滤器返回值判断是否输出日志:
- DENY : 不输出日志;
- ACCEPT : 输出日志;
- NEUTRAL : 中立,即不决定是否输出日志;
常见的过滤器:
- LevelFilter(级别过滤器) : 实现类 ch.qos.logback.classic.filter.LevelFilter
- ThresholdFilter(阈值过滤器):实现类 ch.qos.logback.classic.filter.ThresholdFilter
- EvaluatorFilter(评估者过滤器) : 实现类 ch.qos.logback.core.filter.EvaluatorFilter
- JaninoEventEvaluator过滤器: 实现类 ch.qos.logback.core.filter.EvaluatorFilter
- TurboFilter涡轮过滤器
- DuplicateMessageFilter 重复消息过滤器
可以拿最常见的LevelFilter(级别过滤器) 和ThresholdFilter(阈值过滤器)来进行举例说明:
对于LevelFilter(级别过滤器)来说,设置的级别需要和logger记录器的日志级别是相同的才会来进行进行打印,不同的就不会打印;
对于ThresholdFilter(阈值过滤器)来说,logger记录器的日志级别大于等于过滤器中设置的日志级别才会来进行打印。
下面分别来测试一下对应的使用:
使用级别过滤器来进行测试:
<?xml version="1.0" encoding="UTF-8"?>
INFO
ACCEPT
DENY
%10date %level [%thread] %logger{60} %line %msg %n
尽管日志记录器设置的是debug日志输出级别,但是因为等值匹配的日志级别是INFO,所以对于DEBUG来说是会将其过滤掉的。
对应的测试:
@RequestMapping("findAllUser")
public String findAllUser(){
for (int i = 0; i < 100; i++) {
try {
logger.info("hello,world");
logger.debug("hello,debug");
logger.error("hello,world");
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
logger.info("hello,world");
return "hello,logback";
}
这里分别测试了过滤器中设置的高的和低的日志级别,发现只有INFO日志级别才可以来进行输出。
使用ThresholdFilter(阈值过滤器)来进行筛选过滤:
<?xml version="1.0" encoding="UTF-8"?>
INFO
%10date %level [%thread] %logger{60} %line %msg %n
只有输出级别大于等于INFO才会来进行输出,否则不输出。
4.5、记录器的可加性
之前还有一个属性没有来得及进行介绍,这里也来介绍下,记录器的可加性指的就是日志的重复输出问题。
分别看下对应的问题和解决方式:
<?xml version="1.0" encoding="UTF-8"?>
%10date %level [%thread] %logger{60} %line %msg %n
对应的案例:
@RequestMapping("deleteUser")
public String deleteUser(){
logger.info("hello,world");
return "hello,logback";
}
访问的时候会打印两次,根记录器和父记录器分别户打印一次,因为当前记录器没有设置追加器,所以不会来进行输出:
2022-04-20 11:54:59,543 INFO [http-nio-8080-exec-1] com.guang.springbootlogback.user.controller.UserController 32 hello,world
2022-04-20 11:54:59,543 INFO [http-nio-8080-exec-1] com.guang.springbootlogback.user.controller.UserController 32 hello,world
查看一下对应的记录器树层次结构:
产生原因解释:如果子记录器中的附加器来进行打印的时候,父类记录器中的附加器也会打印日志,直到根记录器中的附加器也会来打印;
那么为了防止这种重复打印的情况,有两种解决方式:
直接设置不可追加
<?xml version="1.0" encoding="UTF-8"?>
%10date %level [%thread] %logger{60} %line %msg %n
第二种情况:直接在根追加器中来进行设置即可:
<?xml version="1.0" encoding="UTF-8"?>
%10date %level [%thread] %logger{60} %line %msg %n
第二种方式使用起来有好处,但是也有弊端。弊端就是都使用一样的日志输出级别,可能对于有些模块来说,是需要使用到debug级别的,比如说mybatis中的日志信息在debug情况下可以来进行打印。
所以在第二种情况的基础之上,我们可以有另外的设置,如下所示:
我们可以利用子记录器来调整对应的输出级别,在输出的时候,对于整个路径下的子类的输出级别都是debug日志级别。
而对于其他的目录级别来说,都是info级别的。
更进步说明的是,我们可以利用过滤器来进行设置