vlambda博客
学习文章列表

spring引入log4j2日志框架

log4j2是什么?

Log4j2是Apache的一个开源项目,通过使用Log4j2,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

可参考:https://baike.baidu.com/item/log4j/480673?fr=aladdin

log4j2可以用干什么?解决什么问题?

log4j2主要用来打印系统中重要的日志信息,解决在系统运营过程中出现的错误日志的记录,可以在不需要修改业务代码,重启web服务,需要修改配置文件就能进行日志拦截的修改。

API文档:http://logging.apache.org/log4j/2.x/log4j-api/apidocs/index.html

log4j、slf4j、log4j2、logback之间的关系

slf4j:slf4j是对所有日志框架制定的一种规范、标准、接口,并不是一个框架的具体的实现,因为接口并不能独立使用,需要和具体的日志框架实现配合使用(如log4j、logback、log4j2)。
log4j:log4j是apache实现的一个开源日志组件。
logback:logback同样是由log4j的作者设计完成的,拥有更好的特性,用来取代log4j的一个日志框架,是slf4j的原生实现。
log4j2:Log4j2是log4j 1.x和logback的改进版,据说采用了一些新技术(无锁异步等),使得日志的吞吐量、性能比log4j 1.x提高了10倍,并解决了一些死锁的bug,而且配置更加简单灵活。
log4j、log4j2、logback的性能比对:

spring引入log4j2日志框架

相关配置说明

Appenders

Appender负责将LogEvents传递到目的地。每个Appender都必须实现Appender接口。大多数Appender继承自AbstractAppender,它增加了Lifecycle和Filterable支持。生命周期允许组件在配置完成后完成初始化并在关闭期间执行清理。Filterable接口允许组件附加过滤器,在事件处理期间对其进行筛选。Appender通常只负责将事件数据写入目标目标。在大多数情况下,他们将格式化事件的责任委托给布局。一些appender包装其他appender,以便他们可以修改LogEvent,处理Appender中的故障,根据高级Filter条件将事件路由到从属Appender

(1)AsyncAppender:引用其他Appender,被引用的Appender可以做到异步输出日志。

(2)CassandraAppender:可以将消息写入Cassandra数据库。

(3)ConsoleAppender:日志写入到标准输出,如System.out或System.error

(4)FailoverAppender:引用一组Appender,如果主的Appender失败则备用Appender开始起作用,直到主Appender恢复正常。

(5)FileAppender:将日志写入文件,比较常用。

(6)FlumeAppender:将日志以event的形式写入flume。

有三种模式:

a.远程客户端模式:模拟flume远程客户端,以avro_event的方式向agent发送消息。

b.内置flume agent模式:直接将event写入flume channel。

c.persist模式:将event写入本地BerkeleyDB,然后通过异步的方式将event发送到flume。

(7)JDBCAppender:使用JDBC连接将数据写入传统数据库。

(8)JMS Appender:将格式化后的日志写入JMS Destination。

(9)HttpAppender:发送日志到一个Http服务,必须使用Layout来格式化日志。

(10)KafkaAppender:将数据发送到kafka的topic,log4j的event对应kafka的record。

(11)MemoryMappedFileAppender:是一种特殊的日志写入方式,将日志写入内存以减少读写磁盘带来的IO开销,提升性能。

(12)NoSQLAppender:可以将数据写入nosql数据库,目前支持MongoDB和CouchDb。

(13)RandomAccessFileAppender:和FileAppender类似,但是使用了ByteBuffer+RandomAccessFile的方式来代替BufferedOutputStream

(14)RewriteAppender:允许LogEvent在其他appender处理之前先由RewriteAppender处理。

(15)RollingFileAppender:配置文件滚动生成策略,按照策略生成新的日志文件。

(16)RollingRandomAccessFileAppender:和RollingFileAppender类似,使用了ByteBuffer+RandomAccessFile的方式代替BufferedOutputStream。

(17)RoutingAppender:路由appender,可以分发Logevent到多个子Appender。

(18)SMTPAppender:将日志以邮件的形式发送,用在错误监控或者报警上。

(19)SocketAppender:将logevent发送到远程机器上,可以使用TCP或者UDP协议。

Layout

layout是指输出Logevent的布局,常见的比如输出日志的级别、时间、类名、线程等信息。Log4j2支持的Layout有如下几种:

(1)CSV Layouts。日志输出为csv文件,如log.info(a,b,c)会输出到csv文件对应的三列。

(2)HTML Layout。将日志输出为html页面,每个Logevent对应table里面的一行。

(3)JSON Layout。将日志输出为json格式。

(4)Pattern Layout。较为常用,通过使用一些匹配规则来确定日志输出格式。

(5)RFC5424 Layout。消息型的Appender经常用这种layout。

(6)Serialized Layout。使用java自身的序列化工具将Logevent序列化成byte array,但因为java固有的安全性问题,这种方式不再被推荐。

(7)Syslog Layout。将日志格式化为BSD syslog格式。

(8)XML Layout。格式化为xml。

配置文件优先级

log4j2.properties>log4j2.yaml>log4j2.json>log4j2.xml>defaultConfiguration

代码下载https://gitee.com/hong99/spring/issues/I1N1DF


代码实现

项目代码结构(非spring纯log4j2)

spring引入log4j2日志框架

引入相关JAR包(版本为最新

<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.13.3</version></dependency><dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.13.3</version></dependency>
import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;

/** * @Auther: csh * @Date: 2020/7/26 14:17 * @Description:纯log4j2打印 */public class Log4j2App { private static final Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
public static void main(String[] args) { logger.trace("trace message"); logger.debug("debug message"); logger.info("info message"); logger.warn("warn message"); logger.error("error message"); logger.fatal("fatal message"); }}

结果

ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.15:22:53.174 [main] ERROR - error message15:22:53.176 [main] FATAL - fatal message

由于上面没有配置log4j2的相关配置,所以打印了 ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.

简单配置文件(基于File)

simple-log4j.xml

<?xml version="1.0" encoding="UTF-8"?><Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </Console> </Appenders>
<Loggers> <Root level="info"> <AppenderRef ref="Console" /> </Root> </Loggers></Configuration>
/** * @Auther: csh * @Date: 2020/7/29 15:58 * @Description:通过java读取配置文件 */public class SimpleXmiLog4j { public static void main(String[] args) throws Exception{ //TODO 注意该路基在每台电脑上面可能不一样! File file =new File("D:\\ideaWorkSpace\\spring\\src\\main\\resources\\log4j\\simple-log4j.xml"); BufferedInputStream in = new BufferedInputStream(new FileInputStream(file)); final ConfigurationSource source = new ConfigurationSource(in); Configurator.initialize(null, source); Logger logger = LogManager.getLogger("myLogger"); logger.trace("trace message"); logger.debug("debug message"); logger.info("info message"); logger.warn("warn message"); logger.error("error message"); logger.fatal("fatal message"); }}

结果

15:59:59.491 [main] INFO myLogger - info message15:59:59.494 [main] WARN myLogger - warn message15:59:59.495 [main] ERROR myLogger - error message15:59:59.495 [main] FATAL myLogger - fatal message

简单配置文件(基于xml的简单配置)

项目结构


spring引入log4j2日志框架

导入maven包

<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.13.3</version></dependency><dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-web</artifactId> <version>2.13.3</version></dependency>

simple-log4j.xml

<?xml version="1.0" encoding="UTF-8"?><Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </Console> </Appenders>
<Loggers> <!--将系统打印所有日志--> <Logger name="com.hong" level="DEBUG"> <AppenderRef ref="Console" /> </Logger> <Logger name="org.springframework" level="info"> <AppenderRef ref="Console" /> </Logger> <Root level="debug"> <!--这儿为trace表示什么都可以打印出来了,其他几个级别分别为:TRACE、DEBUG、INFO、WARN、ERROR和FATAL --> <AppenderRef ref="Console" /> </Root> </Loggers></Configuration>

配置 web.xml

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1" metadata-complete="true"> <display-name>Spring MVC Application</display-name>
<context-param> <param-name>log4jConfiguration</param-name> <param-value>classpath:log4j/simple-log4j.xml</param-value> </context-param> <!-- log4j2-begin --> <listener> <listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class> </listener> <filter> <filter-name>log4jServletFilter</filter-name> <filter-class>org.apache.logging.log4j.web.Log4jServletFilter</filter-class> </filter> <filter-mapping> <filter-name>log4jServletFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping> <!-- log4j2-end -->

<servlet> <servlet-name>HelloWeb</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet>
<servlet-mapping> <servlet-name>HelloWeb</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></web-app>
@Controller@RequestMapping("/")public class HomeController { private static Logger log = LogManager.getLogger(HomeController.class.getName());
@RequestMapping(value = "",method = RequestMethod.GET) public String home(ModelMap model) { log.info("进入了首页!"); model.addAttribute("message","Hello home"); return "home"; }
}

spring引入log4j2日志框架

14:45:28.468 [http-nio-80-exec-1] INFO com.hong.spring.mvc.controller.HomeController - 进入了首页!14:45:28.468 [http-nio-80-exec-1] INFO com.hong.spring.mvc.controller.HomeController - 进入了首页!14:45:29.143 [http-nio-80-exec-2] INFO com.hong.spring.mvc.controller.HomeController - 进入了首页!14:45:29.143 [http-nio-80-exec-2] INFO com.hong.spring.mvc.controller.HomeController - 进入了首页!14:45:29.473 [http-nio-80-exec-3] INFO com.hong.spring.mvc.controller.HomeController - 进入了首页!14:45:29.473 [http-nio-80-exec-3] INFO com.hong.spring.mvc.controller.HomeController - 进入了首页!

将日志写入文件中

新增log4j2-now.xml(将日志写入d盘中)

<?xml version="1.0" encoding="UTF-8"?><!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --><!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出--><!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数--><Configuration status="WARN" monitorInterval="30"> <!--全局属性--> <Properties> <Property name="APP_NAME">spring</Property> <!--设置日志文件存储路径为tomcat/logs/${APP_NAME}--> <Property name="LOG_FILE_PATH">D:/logs/${APP_NAME}</Property> <!--<Property name="LOG_FILE_PATH">./logs/${APP_NAME}</Property>--> <!--设置日志输出格式--> <Property name="PATTERN_FORMAT">%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n</Property> </Properties> <!--配置输出源--> <Appenders> <!--输出到控制台--> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="${PATTERN_FORMAT}"/> </Console>

<File name="applicationLog" fileName="${LOG_FILE_PATH}/MyTestLog.txt" immediateFlush="false" append="true"> <PatternLayout pattern="%d{yy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </File>
<!--输出info日志到文件,filePattern旧日志另存文件名--> <RollingFile name="RollingInfoFile" fileName="${LOG_FILE_PATH}/info.log" filePattern="${LOG_FILE_PATH}/$${date:yyyyMM}/info-%d{yyyyMMdd}-%i.log.gz"> <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <Filters> <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/> <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> </Filters>
<PatternLayout> <pattern>${PATTERN_FORMAT}</pattern> </PatternLayout>
<Policies> <!-- rollover on startup, daily and when the file reaches 10 MegaBytes --> <!--当系统重启/日期更新/超过100M将会覆盖文件--> <OnStartupTriggeringPolicy/> <SizeBasedTriggeringPolicy size="100 MB"/> <TimeBasedTriggeringPolicy/> </Policies> </RollingFile>
<!--输出警告日志到文件--> <RollingFile name="RollingWarnFile" fileName="${LOG_FILE_PATH}/warn.log" filePattern="${LOG_FILE_PATH}/$${date:yyyyMM}/warn-%d{yyyyMMdd}-%i.log.gz"> <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <Filters> <ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/> <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/> </Filters>
<PatternLayout> <pattern>${PATTERN_FORMAT}</pattern> </PatternLayout>
<Policies> <!-- rollover on startup, daily and when the file reaches 10 MegaBytes --> <OnStartupTriggeringPolicy/> <SizeBasedTriggeringPolicy size="100 MB"/> <TimeBasedTriggeringPolicy/> </Policies> </RollingFile>
<!--输出错误日志到文件--> <RollingFile name="RollingErrorFile" fileName="${LOG_FILE_PATH}/error.log" filePattern="${LOG_FILE_PATH}/$${date:yyyyMM}/error-%d{yyyyMMdd}-%i.log.gz"> <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout> <pattern>${PATTERN_FORMAT}</pattern> </PatternLayout>
<Policies> <!-- rollover on startup, daily and when the file reaches 10 MegaBytes --> <OnStartupTriggeringPolicy/> <SizeBasedTriggeringPolicy size="100 MB"/> <TimeBasedTriggeringPolicy/> </Policies> </RollingFile> </Appenders>
<Loggers> <!--过滤掉spring和mybatis的一些无用的DEBUG信息--> <Logger name="org.springframework" level="INFO"/> <Logger name="org.mybatis" level="INFO"/> <Logger name="com.hong.spring" level="info" additivity="true"> <appender-ref ref="applicationLog" /> </Logger>
<!-- LOG everything at INFO level --> <Root level="ALL"> <AppenderRef ref="applicationLog" /> <AppenderRef ref="Console"/> <AppenderRef ref="RollingInfoFile"/> <AppenderRef ref="RollingWarnFile"/> <AppenderRef ref="RollingErrorFile"/> </Root> </Loggers></Configuration>

修改web.xml

<context-param> <param-name>log4jConfiguration</param-name> <!--简单配置--> <!--<param-value>classpath:log4j/simple-log4j.xml</param-value>--> <!--较全配置--> <param-value>classpath:log4j/log4j2-now.xml</param-value></context-param>

结果

[2020-07-31 03:17:39,714] Artifact spring:war: Artifact is deployed successfully[2020-07-31 03:17:39,714] Artifact spring:war: Deploy took 6,880 milliseconds2020-07-31 15:17:40.019 INFO com.hong.spring.mvc.controller.HomeController 17 home - 进入了首页!2020-07-31 15:17:40.041 ERROR com.hong.spring.mvc.controller.HomeController 18 home - error进入了首页!2020-07-31 15:17:40.649 INFO com.hong.spring.mvc.controller.HomeController 17 home - 进入了首页!2020-07-31 15:17:40.650 ERROR com.hong.spring.mvc.controller.HomeController 18 home - error进入了首页!2020-07-31 15:17:40.933 INFO com.hong.spring.mvc.controller.HomeController 17 home - 进入了首页!2020-07-31 15:17:40.934 ERROR com.hong.spring.mvc.controller.HomeController 18 home - error进入了首页!

代码下载https://gitee.com/hong99/spring/issues/I1N1DF


最后

在企业中都是通过一些SLF4J 或commons-loggingp这些门面框架,而不是直接使用log4j或者log4j2这样可以减少因为框架升级或者修改其他日志框架而导致发生大批量代码修改的问题。日志框架对于系统的问题根据和记录非常重要,特别在系统在排除一些异常过程中,通过日志来跟踪异常起到关键性作用,所以日志框架极其重要,比较成熟规模的互联网公司会将日志放到统一的日志分析平台如:kibana或elk等。本文只是关于spring相关,涉及log4j2还是比较浅,后续再统一深入。

相关文献:

http://logging.apache.org/

https://blog.csdn.net/edward0830ly/article/details/8250412

https://juejin.im/post/5c11c831e51d4511624d1b59

http://www.slf4j.org/manual.html