日志
一 、 引言
1.1 日志概念
在计算机领域,日志文件(logfile)是一个记录了发生在运行中的操作系统或其他软件中的事件的文件,或者记录了在网络聊天软件的用户之间发送的消息。
1.2 日志作用
日志是个很重要的东西, 因为程序运行起来以后, 基本上就是一个黑盒子,如果程序的行为和预料的不一致,那就是出现Bug了,如何去定位这个Bug 呢?开发人员能用的工具只有两个。第一种就是单步调试,一步步地跟踪,查看代码中变量的值, 这种办法费时费力, 并且只能在程序员的机器上才能用。第二种就是在特定的地方打印日志, 通过日志的输出,帮助快速定位。
日志记录了系统行为的时间、地点、状态等相关信息,能够帮助我们了解并监控系统状态,在发生错误或者接近某种危险状态时能够及时提醒我们处理,同时在系统产生问题时,能够帮助我们快速的定位、诊断并解决问题。
二 、日志信息级别
日志的级别有很多,不同的日志框架支持的级别不一样,这里就以log4j为例,说说log4j中支持的日志级别。log4j定义了8个级别的log(除去OFF和ALL,可以说分为6个级别),优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。
ALL 最低等级的,用于打开所有日志记录。
TRACE 很低的日志级别,一般不会使用。
DEBUG 指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于开发过程中打印一些运行信息。
INFO 消息在粗粒度级别上突出强调应用程序的运行过程。打印一些你感兴趣的或者重要的信息,这个可以用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避免打印过多的日志。
WARN 表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员的一些提示。
ERROR 指出虽然发生错误事件,但仍然不影响系统的继续运行。打印错误和异常信息,如果不想输出太多的日志,可以使用这个级别。
FATAL 指出每个严重的错误事件将会导致应用程序的退出。这个级别比较高了。重大错误,这种级别你可以直接停止程序了。
OFF 最高等级的,用于关闭所有日志记录。
大部分日志框架都支持ERROR,WARN,INFO,DEBUG级别,这四种级别也是开发中最常用的。
三、日志发展历程
3.1 System.out和System.err
这是最早的日志记录方式,但是不灵活也不可配置,要么就是全部打印,要么就是全部不打印,没有一个统一的日志级别
3.2 Log4j
Ceki Gülcü (Lo4j的主要贡献者)于2001年发布了Log4j,后来成为Apache 基金会的顶级项目。Log4j 在设计上非常优秀,对后续的 Java Log 框架有长久而深远的影响,它定义的Logger、Appender、Level等概念如今已经被广泛使用。Log4j 的短板在于性能,在Logback 和 Log4j2 出来之后,Log4j的使用也减少了。
3.3 JUL
受Logj启发,Sun在Java1.4版本中引入了java.util.logging,但是j.u.l功能远不如log4j完善,开发者需要自己编写Appenders(Sun称之为Handlers),且只有两个Handlers可用(Console和File),j.u.l在Java1.5以后性能和可用性才有所提升。
3.4 JCL
由于项目的日志打印必然选择两个框架中至少一个,这时候,Apache的JCL(commons-logging)诞生了。JCL 是一个Log Facade,只提供 Log API,不提供实现,然后有 Adapter 来使用 Log4j 或者 JUL 作为Log Implementation。在程序中日志创建和记录都是用JCL中的接口,在真正运行时,会看当前ClassPath中有什么实现,如果有Log4j 就是用 Log4j, 如果啥都没有就是用 JDK 的 JUL。
JCL结构图
不过,commons-logging对Log4j和j.u.l的配置问题兼容的并不好,使用commons-loggings还可能会遇到类加载问题,导致NoClassDefFoundError的错误出现。
3.5 Slf4j
Ceki Gülcü(也就是Log4j的作者)由于一些原因离开了Apache,之后觉得JCL不好,于是于2005年自己撸出一个新工程,也就是一套新日志接口(有得也叫日志门面):Slf4j(Simple Logging Facade for Java),感觉粗来了么。。。这战争的硝烟,明显这个Slf4j是直指JCL啊,但是后面确实也证明了Slf4j是要比JCL在很多地方更优秀。
但是由于Slf4j出来的较晚,而且还只是一个日志接口,所以之前已经出现的日志产品,如JCL和Log4j都是没有实现这个接口的,所以尴尬的是光有一个接口,没有实现的产品也是很憋屈啊,就算开发者想用Slf4j也是用不了,这时候Ceki Gülcü想出一个桥接包模式,要让Sun或者Apache这两个庞然大物来实现我的接口。
Slf4j结构图
提供了桥接包后的结构
slf4j桥接包后的结构图
官方提供sl4j整合各种日志产品的图
sl4j整合各种日志产品的图
3.6 Logback
由于使用Slf4j,需要一次桥接包,也就是之前的日志产品都不是正统的Slf4j的实现,因此,2006年,出自Ceki Gülcü之手的日志产品Logback应运而生, logback是完美实现了Slf4j,于是现在日志系统变成了下面这样。
Logback结构图
Logback是log4j的升级版,当前分为三个目标模块:
- logback-core:核心模块,是其它两个模块的基础模块
- logback-classic:是log4j的一个改良版本,同时完整实现 SLF4J API 使你可以很方便地更换成其它日记系统如log4j 或 JDK14 Logging
- logback-access:访问模块与Servlet容器集成提供通过Http来访问日记的功能,是logback不可或缺的组成部分
Logback相较于log4j有更多的优点:
-
更快的执行速度
-
更充分的测试
-
logback-classic 非常自然的实现了SLF4J
-
使用XML配置文件或者Groovy
-
自动重新载入配置文件
-
优雅地从I/O错误中恢复
-
自动清除旧的日志归档文件
-
自动压缩归档日志文件
-
谨慎模式
-
Lilith
-
配置文件中的条件处理
-
更丰富的过滤
ok了,现在咱们有了2个日志接口,3个日志产品,大家也都看起来相安无事。。。但。。。Slf4j+Logback的模式,显然很冲击JCL+Log4j,并且本身Logback确实比Log4j性能更优,设计更为合理,所以。。。老东家Apache可就坐不住了。
3.7 log4j2
现在有了更好的 SLF4J 和 Logback,慢慢取代JCL 和 Log4j ,事情到这里总该大统一圆满结束了吧。然而维护 Log4j 的人不这样想,他们不想坐视用户一点点被 SLF4J / Logback 蚕食,继而搞出了 Log4j2。
3.8 小结
常见的Java日志框架
- log4j
- logback
- j.u.l (java.util.logging)
常见的Java日志门面
- SLF4J
- commons-logging
四、JCL+log4j
4.1 上手案例
4.1.1 添加依赖
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- JCL依赖 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- log4j依赖 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
4.1.2 配置文件
在classpath下面新建属性文件log4j.properties,内容如下。
### 指定日志输出级别 ###
log4j.rootLogger = debug,stdout
### 指定输出到控制台 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
### 指定使用流的方式输出 ###
log4j.appender.stdout.Target=System.out
### 指定日志输出布局 ###
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
### 指定日志编码 ###
log4j.appender.stdout.Encoding=UTF-8
### 指定日志格式 ###
log4j.appender.stdout.layout.ConversionPattern= %d{yyy-MM-dd HH\:mm\:ss} %p %c {0}\: %L - %m%n
ConversionPattern 参数值说明
-X号: X信息输出时左对齐;
%p: 输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL,
%d: 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
%r: 输出自应用启动到输出该log信息耗费的毫秒数
%c: 输出日志信息所属的类目,通常就是所在类的全名
%t: 输出产生该日志事件的线程名
%l: 输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main (TestLog4.java:10)
%x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
%%: 输出一个"%"字符
%F: 输出日志消息产生时所在的文件名称
%L: 输出代码中的行号
%m: 输出代码中指定的消息,产生的日志具体信息
%n: 输出一个回车换行符,Windows平台为"/r/n",Unix平台为"/n"输出日志信息换行
4.1.3 API测试
public class Log4jTest {
// 1.创建日志对象
private static Log log = LogFactory.getLog(Log4jTest.class);
@Test
public void testLog(){
// 2.调用不同的级别输出日志
log.debug("这是debug级别");
log.info("这是info级别");
log.warn("这是warn级别");
log.error("这是error级别");
}
}
4.1.4 运行结果
运行结果
4.2 Appender 参数值
Log4j日志系统还提供许多强大的功能,比如允许把日志输出到不同的地方,如控制台(Console)、文件(Files)等,可以根据天数或者文件大小产生新的文件,可以以流的形式发送到其它地方等等。
常使用的类如下:
类名 | 说明 |
---|---|
org.apache.log4j.ConsoleAppender | 日志输出控制台 |
org.apache.log4j.FileAppender | 日志输出到文件 |
org.apache.log4j.DailyRollingFileAppender | 每天产生一个日志文件 |
org.apache.log4j.RollingFileAppender | 文件大小到达指定尺寸的时候产生一个新的文件 |
org.apache.log4j.net.SMTPAppender | 将日志以已邮件的形式发送出去 |
4.3 Layout 参数值
有时用户希望根据自己的喜好格式化自己的日志输出,Log4j可以在Appenders的后面附加Layouts来完成这个功能。Layouts提供四种日志输出样式,如根据HTML样式、自由指定样式、包含日志级别与信息的样式和包含日志时间、线程、类别等信息的样式。
常使用的类如下:
类名 | 说明 |
---|---|
org.apache.log4j.HTMLLayout | 以HTML表格形式布局 |
org.apache.log4j.PatternLayout | 可以灵活地指定布局模式 |
org.apache.log4j.SimpleLayout | 包含日志信息的级别和信息字符串 |
org.apache.log4j.TTCCLayout | 含日志产生的时间、线程、类别等信息 |
4.4日志输出到控制台
### #配置根Logger ###
log4j.rootLogger = debug,stdout
### 指定输出到控制台 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
### 指定使用流的方式输出 ###
log4j.appender.stdout.Target=System.out
### 指定日志输出布局 ###
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
### 指定日志编码 ###
log4j.appender.stdout.Encoding=UTF-8
运行结果
4.5日志输出到文件
log4j.rootLogger = debug,E
##输出到日志文件
log4j.appender.E= org.apache.log4j.FileAppender
# 日志输出位置
log4j.appender.E.File = ./log/log.log
# 日志是否追加
log4j.appender.E.Append = true
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
运行结果
4.6 每天一个日志文件
log4j.rootLogger = debug,E
##每天一个日志文件
log4j.appender.E= org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File = ./log/log.log
log4j.appender.E.Append = true
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
运行结果
4.7 按照文件大小产生文件
log4j.rootLogger = debug,E
# 文件大小到达指定尺寸的时候产生一个新的文件
log4j.appender.E= org.apache.log4j.RollingFileAppender
log4j.appender.E.File = ./log/log.log
log4j.appender.E.Append = true
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
# 当文件大小超过256KB时,新创建一个文件接收日志
log4j.appender.E.MaxFileSize=2KB
## 保存10个备份文件。
log4j.appender.E.MaxBackupIndex=10
运行结果
4.8 邮件发送日志
因为这里需要发送邮件,所以我们需要添加邮件相关的依赖包
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4</version>
</dependency>
log4j.rootLogger = debug,MAIL
#自定义的Appender为邮件发送
log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender
# 发送邮件的日志级别
log4j.appender.MAIL.Threshold=ERROR
#缓存文件大小,日志达到512K时发送Email
log4j.appender.MAIL.BufferSize=512
#发件人
log4j.appender.MAIL.From=13480743727@163.com
#发送邮件的服务器
log4j.appender.MAIL.SMTPHost=smtp.163.com
#发送邮件箱的用户
log4j.appender.MAIL.SMTPUsername=13480743727@163.com
#发送邮件箱的密码
log4j.appender.MAIL.SMTPPassword=dsx123456
#邮件的标题,log4j默认的读取是iso-8859-1,所以这里直接写中文会出现乱码情况
# 如果标题有中文用native2asii转换一下编码方式
log4j.appender.MAIL.Subject=\u65e5\u5fd7\u544a\u8b66\uff0c\u7cfb\u7edf\u51fa\u73b0\u9519\u8bef
# 收件人里面写上自己,否则会被163认为是垃圾邮件,导致发送失败。
log4j.appender.MAIL.To=854569279@qq.com,13480743727@163.com
log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout
log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n
4.9 Log4j XML配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//log4j/log4j Configuration//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- 日志输出到控制台 -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<!-- 日志输出格式 -->
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%p][%d{yyyy-MM-dd HH:mm:ss SSS}][%c]-[%m]%n"/>
</layout>
<!--过滤器设置输出的级别-->
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<!-- 设置日志输出的最小级别 -->
<param name="levelMin" value="INFO"/>
<!-- 设置日志输出的最大级别 -->
<param name="levelMax" value="ERROR"/>
</filter>
</appender>
<!-- 输出日志到文件 -->
<appender name="fileAppender" class="org.apache.log4j.FileAppender">
<!-- 输出文件全路径名-->
<param name="File" value="./log/log.log"/>
<!--是否在已存在的文件追加写:默认时true,若为false则每次启动都会删除并重新新建文件-->
<param name="Append" value="true"/>
<param name="Threshold" value="INFO"/>
<!--是否启用缓存,默认false-->
<param name="BufferedIO" value="false"/>
<!--缓存大小,依赖上一个参数(bufferedIO), 默认缓存大小8K -->
<param name="BufferSize" value="512"/>
<!-- 日志输出格式 -->
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%p][%d{yyyy-MM-dd HH:mm:ss SSS}][%c]-[%m]%n"/>
</layout>
</appender>
<!-- 输出日志到文件,当文件大小达到一定阈值时,自动备份 -->
<appender name="rollingAppender" class="org.apache.log4j.RollingFileAppender">
<!-- 日志文件全路径名 -->
<param name="File" value="./log/log.log" />
<!--是否在已存在的文件追加写:默认时true,若为false则每次启动都会删除并重新新建文件-->
<param name="Append" value="true" />
<!-- 保存备份日志的最大个数,默认值是:1 -->
<param name="MaxBackupIndex" value="10" />
<!-- 设置当日志文件达到此阈值的时候自动回滚,单位可以是KB,MB,GB,默认单位是KB,默认值是:10MB -->
<param name="MaxFileSize" value="1KB" />
<!-- 设置日志输出的样式 -->
<layout class="org.apache.log4j.PatternLayout">
<!-- 日志输出格式 -->
<param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss:SSS}] [%-5p] [method:%l]%n%m%n%n" />
</layout>
</appender>
<!-- 日志输出到文件,每天一个日志文件 -->
<appender name="dailyRollingAppender" class="org.apache.log4j.DailyRollingFileAppender">
<!-- 文件文件全路径名 -->
<param name="File" value="./log/log.log"/>
<param name="Append" value="true" />
<!-- 设置日志备份频率,默认:为每天一个日志文件 -->
<param name="DatePattern" value="'.'yyyy-MM-dd'.log'" />
<!--每分钟一个备份-->
<!--<param name="DatePattern" value="'.'yyyy-MM-dd-HH-mm'.log'" />-->
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%p][%d{HH:mm:ss SSS}][%c]-[%m]%n"/>
</layout>
</appender>
<!-- 发邮件(只有ERROR时才会发送!) -->
<appender name="MAIL" class="org.apache.log4j.net.SMTPAppender">
<!-- 日志的错误级别 -->
<param name="threshold" value="error" />
<!-- 缓存文件大小,日志达到512K时发送Email -->
<param name="BufferSize" value="512" /><!-- 单位K -->
<param name="From" value="13480743727@163.com" />
<param name="SMTPHost" value="smtp.163.com" />
<param name="Subject" value="juyee-log4jMessage" />
<param name="To" value="854569279@qq.com,13480743727@163.com" />
<param name="SMTPUsername" value="13480743727@163.com" />
<param name="SMTPPassword" value="dsx123456" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%-d{yyyy-MM-dd HH:mm:ss.SSS} [%p]-[%c] %m%n" />
</layout>
</appender>
<!--
单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等
name:包名
additivity:是否继承root配置
-->
<logger name="com.qf.test.log4j" additivity="false">
<!--这个级别是对console的二次过滤 -->
<level value ="error"/>
<appender-ref ref="console"/>
</logger>
<!-- 根logger的设置,若代码中未找到指定的logger,则会根据继承机制,使用根logger-->
<root>
<appender-ref ref="console"/>
<appender-ref ref="fileAppender"/>
<appender-ref ref="rollingAppender"/>
<appender-ref ref="dailyRollingAppender"/>
<appender-ref ref="MAIL"/>
</root>
</log4j:configuration>
五、SL4J+logback
5.1 上手案例
5.1.1 添加依赖
<properties>
<slf4j-api.version>1.6.4</slf4j-api.version>
<logback-classic.version>0.9.28</logback-classic.version>
<jcl-over-slf4j.version>1.6.4</jcl-over-slf4j.version>
</properties>
<dependencies>
<!-- 日志框架API -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<!-- 日志实现提供者 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback-classic.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>
<!-- 拦截 apache commons logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${jcl-over-slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
5.1.2 配置文件
在classpath下面新建Logback.xml文件,内容如下。
<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,
默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。
默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
<!-- 应用名称 -->
<property name="APP_NAME" value="log-demo" />
<!--日志文件的保存路径,首先查找系统属性-Dlog.dir,如果存在就使用其;否则,
在当前目录下创建名为logs目录做日志存放的目录 -->
<property name="LOG_HOME" value="D:/code/ideaworkspace3/log-demo/log" />
<!-- 日志输出格式 -->
<property name="ENCODER_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n" />
<contextName>${APP_NAME}</contextName>
<!-- 控制台日志:输出全部日志到控制台 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>${ENCODER_PATTERN}</Pattern>
</encoder>
</appender>
<!-- 文件日志:输出全部日志到文件 -->
<appender name="file"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<Ecndoing>UTF_8</Ecndoing>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/output1.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志保留时间 7天-->
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${ENCODER_PATTERN}</pattern>
</encoder>
</appender>
<!-- 错误日志:用于将错误日志输出到独立文件 -->
<appender name="error_file"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${ENCODER_PATTERN}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>error</level>
</filter>
</appender>
<!-- 指定包下的class指定不同的日志级别等-->
<logger name="com.qf.sl4j.test.Sl4jTest" level="info" >
<appender-ref ref="console" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="console" />
<appender-ref ref="file" />
<appender-ref ref="error_file" />
</root>
</configuration>
5.1.3 API测试
package com.qf.sl4j.test;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Sl4jTest {
private final static Logger log = LoggerFactory.getLogger(Sl4jTest.class);
@Test
public void testSl4j(){
log.debug("这是debug级别");
log.info("这是info级别");
log.warn("这是warn级别");
log.error("这是错误级别1");
}
}
5.1.4 运行结果
运行结果