本文共 9654 字,大约阅读时间需要 32 分钟。
spring boot整合log4j2项目spring-boot-jsp源码:
spring-boot相关项目源码,
码云地址:
github地址:
对于我们开发人员来说,日志记录往往不被重视。在生产环境中,日志是查找问题来源的重要依据。日志可记录程序运行时产生的错误信息、状态信息、调试信息和执行时间信息等多种多样的信息。可以在程序运行出现错误时,快速地定位潜在的问题源。
目前常用的日志框架有java.util.logging、commons logging、slf4j、log4j1.x、logback、log4j2.x 等若干种,这里我们使用log4j2框架进行日志管理,先介绍下日志框架的发展史以及优点。
1996年早期,欧洲安全电子市场项目组决定编写它自己的程序跟踪API(Tracing API)。经过不断的完善,这个API终于成为一个十分受欢迎的Java
日志软件包,即log4j。后来log4j成为Apache基金会项目中的一员。
期间log4j近乎成了Java社区的日志标准。据说Apache基金会还曾经建议Sun引入log4j到java的标准库中,但Sun拒绝了。
2002年Java1.4发布,Sun推出了自己的日志库jul(java util logging),其实现基本模仿了log4j的实现。在JUL出来以前,log4j就已经成为
一项成熟的技术,使得log4j在选择上占据了一定的优势。
接着,Apache推出了jakarta commons logging,jcl只是定义了一套日志接口(其内部也提供一个simple log的简单实现),支持运行时动态加载日志组件的实现,也就是说,在你的应用代码里,只需调用commons logging的接口,底层实现可以是log4j,也可以是java util logging。
后来(2006年),Ceki Gülcü(Gulcu)不适应Apache的工作方式,离开了Apache。然后先后创建了slf4j(日志门面接口,类似于commons logging)
和logback(slf4j的实现)两个项目,并回瑞典创建了QOS公司,QOS官网上是这样描述logback的:The Generic,Reliable Fast&FlexibleLogging Framework(一个通用,可靠,快速且灵活的日志框架)。
现今,Java日志领域被划分为两大阵营:commons logging阵营和slf4j阵营。 commons logging在Apache大树的笼罩下,有很大的用户基数。
但有证据表明,形式正在发生变化。2013年底有人分析了GitHub上30000个项目,统计出了最流行的100个Libraries,可以看出slf4j的发展
趋势更好。如下图所示:
Apache眼看有被logback反超的势头,于2012年7月重写了log4j 1.x,成立了新的项目log4j2.x。log4j2在各个方面都与logback非常相似。
这些日志系统有什么区别,开发时如何选择适合自己项目的日志系统呢?日志系统可分为两类。
一类:只提供接口不提供实现,如 Apache Commons Logging 和 slf4j。这类日志系统需要和具体的日志系统一起使用,优点是可以自由切换不同的日志实现系统。如果你的项目以后要打成 jar 包被别的系统使用或者你以后可能更换日志系统,通常使用这一类日志系统。
另一类:是具体的日志系统实现。如 log4j1.x、logback、log4j2.x、java.util.logging等。
下图清晰的展示了 slf4j 和其它日志系统的关系:
slf4j(SimpleLogging Facade for Java),面向 java 的简单日志门面,人如其名,slf4j 是其他日志的门面,它提供统一的接口,并不提供实现,不是具体的日志系统。
log4j1 曾经被广泛使用,2015年8月已停止更新,logback和log4j2作为它的替代者,拥有更好的性能,有很多的改进,下面重点讲一下 logback 和 log4j2 的特性。
1、 性能的提升。Logback 的内核重写了,在某些特定的场景上性能提升 10 倍以上,同时所需的内存更加少。
2、 非常充分的测试。Logback经过了几年,数不清小时的测试,与log4j1的测试相比不在同一个级别,因此 logback 更稳定可靠。
3、 非常自然的实现了slf4j。而log4j1 和 slf4j 一起使用需要适配层。
4、 自动重新加载配置文件。当配置文件修改了,Logback-classic能自动重新加载配置文件,不需要重启服务器。
5、 优雅地从I/O错误中恢复。如果一个文件服务器临时宕机,你不需要重启应用,日志功能就能正常工作。
6、 配置文件可适应多环境。通常在开发、测试、生产环境,需要变换日志的配置文件。而在不同环境下,配置文件只有一些很小的不同,为了避免重复,logback支持使用,和进行条件处理,同一个配置文件就可以在不同的环境中使用了。
7、 过滤器。生产环境中,有时需要低级别的日志来查明问题,在log4j1 中,只有降低日志级别,这样的话会打出大量的日志而影响性能。而logback 中,你可以继续保持那个日志级别而除掉某种特殊情况。
8、 自动压缩归档日志文件。压缩通常是异步执行的,所以即使是很大的日志文件,你的应用都不会因此而被阻塞。
9、 通过配置自动去除旧的日志文件。
log4j2在各个方面都与logback非常相似,那么为什么我们还需要log4j 2呢?
1、 可配置的审计型日志。log4j1和logback在重新配置的时候会丢失之前的日志文件,log4j2不会。log4j2自身内部报的exception会被发现,但是log4j1和logback不会。
2、 下一代异步logger。log4j 2是基于LMAXDisruptor库的(一个用于在线程间通信的高效低延迟且简单的框架),在多线程场景下,它的日志吞吐量比其他框架多出10倍以上。
3、 可运行在免垃圾收集模式。这样可以减少垃圾收集器的压力和提供更好的响应时间性能。
4、 插件式结构。可根据自己的需要扩展框架,可以实现自己的logger、appenders、filters、layouts、lookups 和patternconverters,而无需对log4j2做任何更改。
5、 Java 5的并发性。log4j2利用Java 5中的并发特性支持,尽可能地执行最低层次的加锁,log4j1中存留的死锁问题,很多已经在logback中解决,但logback的很多类仍然保持着较高层次的同步。如果你的程序仍然饱受内存泄漏的折磨,请毫不犹豫地试一下log4j2。
下图为Apache Logging PMC成员Christian Grobmeier 在2013年7月发表的一篇标题为《Log4j2:性能几近于疯狂》的文章;
使用spring-boot-jsp项目,项目源码:
导入依赖
<!-- 导入log4j2依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- log4j2支持异步日志,导入disruptor依赖 -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.6</version>
</dependency>
配置文件:在项目目录/resources资源文件根目录下创建 log4j2.xml 文件,详细配置如下:
<?xmlversion="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE >ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出;可以设置成OFF(关闭)或Error(只输出错误信息)-->
<!--monitorInterval:Log4j2能够自动检测修改配置文件和重新配置本身,设置间隔秒数-->
<Configurationstatus="WARN" monitorInterval="30">
<Properties>
<!-- 缺省配置(用于开发环境),配置日志文件输出目录和动态参数。其他环境需要在VM参数中指定;
“sys:”表示:如果VM参数中没指定这个变量值,则使用本文件中定义的缺省全局变量值 -->
<Propertyname="instance">spring-boot-log</Property>
<Propertyname="log.dir">D:\log\logs</Property>
</Properties>
<!-- 定义所有的appender -->
<Appenders>
<!--这个输出控制台的配置-->
<Console name="Console"target="SYSTEM_OUT">
<!--输出日志的格式-->
<PatternLayout
pattern="[%date{yyyy-MM-ddHH:mm:ss.SSS}][%thread][%level][%class][%line]:%message%n" />
</Console>
<!-- info及以上级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingRandomAccessFilename="infoLog"
fileName="${log.dir}/${instance}-info.log"
filePattern="${log.dir}/%d{yyyy-MM}/${instance}-info-%d{yyyy-MM-dd}-%i.log.gz"
append="true">
<PatternLayout
pattern="[%date{yyyy-MM-ddHH:mm:ss.SSS}][%thread][%level][%class][%line]:%message%n" />
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<Filters>
<ThresholdFilterlevel="info" onMatch="ACCEPT"
onMismatch="NEUTRAL"/>
</Filters>
<Policies>
<!-- 基于时间的滚动策略,interval属性用来指定多久滚动一次,默认是1 hour -->
<TimeBasedTriggeringPolicyinterval="1"
modulate="true" />
<!-- 基于指定文件大小的滚动策略,size属性用来定义每个日志文件的大小 -->
<SizeBasedTriggeringPolicysize="1MB" />
<!-- DefaultRolloverStrategy:用来指定同一个文件夹下最多有几个日志文件时开始删除最旧的,创建新的(通过max属性) -->
</Policies>
</RollingRandomAccessFile>
<!-- warn级别的日志信息 -->
<RollingRandomAccessFilename="warnLog"
fileName="${log.dir}/${instance}-warn.log"
filePattern="${log.dir}/%d{yyyy-MM}/${instance}-warn-%d{yyyy-MM-dd}-%i.log.gz"
append="true">
<Filters>
<ThresholdFilterlevel="error" onMatch="DENY"
onMismatch="NEUTRAL"/>
<ThresholdFilterlevel="warn" onMatch="ACCEPT"
onMismatch="DENY" />
</Filters>
<PatternLayout
pattern="[%date{yyyy-MM-ddHH:mm:ss.SSS}][%thread][%level][%class][%line]:%message%n" />
<Policies>
<TimeBasedTriggeringPolicyinterval="1"
modulate="true" />
<SizeBasedTriggeringPolicysize="1MB" />
</Policies>
</RollingRandomAccessFile>
<!-- error级别的日志信息 -->
<RollingRandomAccessFilename="errorLog"
fileName="${log.dir}/${instance}-error.log"
filePattern="${log.dir}/%d{yyyy-MM}/${instance}-error-%d{yyyy-MM-dd}-%i.log.gz"
append="true">
<Filters>
<ThresholdFilterlevel="ERROR" onMatch="ACCEPT"
onMismatch="DENY" />
</Filters>
<PatternLayout
pattern="[%date{yyyy-MM-ddHH:mm:ss.SSS}][%thread][%level][%class][%line]:%message%n" />
<Policies>
<TimeBasedTriggeringPolicyinterval="1"
modulate="true" />
<SizeBasedTriggeringPolicysize="1MB" />
</Policies>
</RollingRandomAccessFile>
</Appenders>
<!-- 全局配置,默认所有的Logger都继承此配置 -->
<!-- 用来配置LoggerConfig,包含一个root logger和若干个普通logger。
additivity指定是否同时输出log到父类的appender,缺省为true。
一个Logger可以绑定多个不同的Appender。只有定义了logger并引入的appender,appender才会生效。 -->
<Loggers> <!-- 第三方的软件日志级别 -->
<loggername="org.springframework" level="info"additivity="true">
<AppenderRef ref="warnLog"/>
<AppenderRef ref="errorLog"/>
</logger>
<loggername="java.sql.PreparedStatement" level="debug"
additivity="true">
<AppenderRef ref="Console"/>
</logger>
<!-- root logger 配置 -->
<Root level="info"includeLocation="true">
<AppenderRef ref="infoLog"/>
<AppenderRef ref="Console"/>
<AppenderRef ref="errorLog"/>
</Root>
<!-- AsyncRoot - 异步记录日志 - 需要LMAXDisruptor的支持 -->
<!-- <AsyncRootlevel="info" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="infoLog"/>
<AppenderRef ref="errorLog"/>
</AsyncRoot> -->
</Loggers>
</Configuration>
用于测试,所以SizeBasedTriggeringPolicy日志文件大小最大设置为1MB;实际使用根据情况调整;
Controller类:
@RequestMapping("/wyait")
@ResponseBody
public String getMsg(HttpServletResponseresponse) {
LOGGER.debug("===========debug信息>>>>"+ paramProperties);
LOGGER.info("===========info信息>>>>"+ paramProperties);
LOGGER.trace("I am trace log.");
LOGGER.debug("I am debug log.");
LOGGER.warn("I am warn log.");
LOGGER.error("I am error log.");
// 手动异常
System.out.println(1 / 0);
// 会有中文乱码问题 TODO
return paramProperties.getWyaitName() +" 正在写"
+ paramProperties.getWyaitTitle() +"!总结:"
+ paramProperties.getWyaitMessage();
}
启动项目,访问:http://127.0.0.1:8080/cat/wyait
查看日志目录:
(1)根节点Configuration有两个属性:status和monitorinterval,有两个子节点:Appenders和Loggers(表明可以定义多个Appender和Logger).
status用来指定log4j本身的打印日志的级别.
monitorinterval用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s.(如果更改配置文件,不用重启系统)
(2)Appenders节点,常见的有三种子节点:Console、RollingFile、File.
Console节点用来定义输出到控制台的Appender.
name:指定Appender的名字.
target:SYSTEM_OUT 或 SYSTEM_ERR,一般只设置默认:SYSTEM_OUT.
PatternLayout:输出格式,不设置默认为:%m%n.
File节点用来定义输出到指定位置的文件的Appender.
name:指定Appender的名字.
fileName:指定输出日志的目的文件带全路径的文件名.
PatternLayout:输出格式,不设置默认为:%m%n.
RollingFile节点用来定义超过指定大小自动删除旧的创建新的的Appender.
name:指定Appender的名字.
fileName:指定输出日志的目的文件带全路径的文件名.
PatternLayout:输出格式,不设置默认为:%m%n.
filePattern:指定新建日志文件的名称格式.
Policies:指定滚动日志的策略,就是什么时候进行新建日志文件输出日志.
TimeBasedTriggeringPolicy:Policies子节点,基于时间的滚动策略,interval属性用来指定多久滚动一次,默认是1 hour。modulate=true用来调整时间:比如现在是早上3am,interval是4,那么第一次滚动是在4am,接着是8am,12am...而不是7am.
SizeBasedTriggeringPolicy:Policies子节点,基于指定文件大小的滚动策略,size属性用来定义每个日志文件的大小.
DefaultRolloverStrategy:用来指定同一个文件夹下最多有几个日志文件时开始删除最旧的,创建新的(通过max属性)。
(3)Loggers节点,常见的有两种:Root和Logger.
Root节点用来指定项目的根日志,如果没有单独指定Logger,那么就会默认使用该Root日志输出
level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn <Error < Fatal < OFF.
AppenderRef:Root的子节点,用来指定该日志输出到哪个Appender.
Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。
level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn <Error < Fatal < OFF.
name:用来指定该Logger所适用的类或者类所在的包全路径,继承自Root节点.
AppenderRef:Logger的子节点,用来指定该日志输出到哪个Appender,如果没有指定,就会默认继承自Root.如果指定了,那么会在指定的这个Appender和Root的Appender中都会输出,此时我们可以设置Logger的additivity="false"只在自定义的Appender中进行输出。
(4)关于日志level.
共有8个级别,按照从低到高为:All< Trace < Debug < Info < Warn < Error < Fatal < OFF.
All:最低等级的,用于打开所有日志记录.
Trace:是追踪,就是程序推进以下,你就可以写个trace输出,所以trace应该会特别多,不过没关系,我们可以设置最低日志级别不让他输出.
Debug:指出细粒度信息事件对调试应用程序是非常有帮助的.
Info:消息在粗粒度级别上突出强调应用程序的运行过程.
Warn:输出警告及warn以下级别的日志.
Error:输出错误信息日志.
Fatal:输出每个严重的错误事件将会导致应用程序的退出的日志.
OFF:最高等级的,用于关闭所有日志记录.
程序会打印高于或等于所设置级别的日志,设置的日志等级越高,打印出来的日志就越少。
本文转自 wyait 51CTO博客,原文链接:http://blog.51cto.com/wyait/1969613,如需转载请自行联系原作者