log4j日志保存服务器

在之前的『从0到1理解Java Web』的『日志框架』部分,我们知道了如何让项目打印日志在控制台上,方便我们调试观察代码的运行,这篇文章主要是介绍如何把日志打印输出到服务器文件上保存起来,上线后借助日志文件找到Bug。

这里分3部分,一个是流的概念,一个是log4j配置,另一个是打印spring自身的日志。

在linux中有流的概念,从一个地方流入(比如键盘,比如扫描仪,比如文件),流出到另一个地方(比如控制台,比如文件,比如屏幕),上一次我们实现了『从日志系统流出到控制台』,这次实现『从日志系统流出到文件』即可。

把日志的去向从控制台改到文件,需要改动哪里?这牵扯到第二部分,也就是log4j的配置。

log4j配置

log4j有3个组件,loggers/appenders/layouts,它们一起协同工作,指定了日志内容的格式,根据日志的类型与级别,将日志输出到指定的位置。

loggers

指定日志输出的最低级别,把级别信息传给appenders。
日志级别由低到高分别是:Trace < Debug < Info < Warn < Error < Fatal,只记录大于等于当前级别的信息。

appenders

指定日志的输出方式与保存位置。
主要有以下几类appender:
1.ConsoleAppender :将日志输出至控制台
2.DailyRollingFileAppender :将日志输出至文件,日志文件每天切割
3.FileAppender :将日志输出至文件
4.RollingFileAppender :将日志输出至文件,日志文件按指定的大小进行切割
5.WriterAppender :将日志以信息流的格式发送到指定位置

layouts

指定日志内容的格式,比如输出为Html等。

这3个组件的具体配置都在log4j.xml中。把lo4j4.xml修改为以下的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?xml version="1.0" encoding="UTF-8"?>

<Configuration status="info" packages="com.dianping">
<Properties>
<!--日志输入路径-->
<Property name="log-path">/data/applogs/${sys:app.name}/logs</Property>
<!--日志输出到cat格式-->
<Property name="pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [${sys:app.name} ${hostName}}] [%-5level] [%t] [%c] - %msg%xEx%n</Property>
<!--日志进入存储中心的格式-->
<Property name="dw-pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} ${hostName} ${sys:app.name} %p %t %c %msg%xEx%n</Property>
<Property name="scribeCategory">${sys:app.name}</Property> <!--日志需要和日志采集的一致-->
</Properties>

<Appenders>
<!-- 记录除用户行为日志外的所有信息 -->
<RollingFile name="appAppender" fileName="${log-path}/app.log" filePattern="${log-path}/app.log.%d{yyyy-MM-dd}">
<PatternLayout pattern="${pattern}"/>
<Policies>
<!-- 这里的interval由filePattern中的最小时间单位来决定,如果filePattern 中是天,这1就是1天-->
<TimeBasedTriggeringPolicy interval="1"/>
</Policies>
</RollingFile>
<!-- 独立的 error appender 便于排查问题 -->
<RollingFile name="errorAppender" fileName="${log-path}/error.log"
filePattern="${log-path}/error.log.%d{yyyy-MM-dd}">
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${pattern}"/>
<Policies>
<TimeBasedTriggeringPolicy interval="1"/>
</Policies>
</RollingFile>
<!--打印日志到控制台,方便调试-->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level [%c] - %msg%n"/>
</Console>
</Appenders>

<Loggers>
<Root level="INFO">
<AppenderRef ref="appAppender"/>
<AppenderRef ref="errorAppender"/>
<AppenderRef ref="Console"/>
</Root>
</Loggers>

</Configuration>

另外建立classpath:META-INF下建立文件app.properties(即mai/resources/METa-INF下),在里面指定项目的名称,内容如下:

1
app.name=my-project-name

随后启动项目,进入/data/applogs/my-project-name/logs目录,即可看到日志文件。

到这里,我们已经成功地将代码里的日志输出到文件里。每天保存,可是,如果你仔细点观察,会发现日志文件里并没有spring和其它第三方库的日志,这可是大问题,很多时候Bug是spring的bean注入失败或找不到对应的类引起的,如果没有spring的日志,我们很难追查到真正原因。

监听web运行中第三方库日志(如spring)

监听web运行时的日志,那么我们就能监听到所有日志,包括spring和其它技术栈的日志。

在web项目配置文件web.xml中加入对应的监听器,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<context-param>
<param-name>log4jContextName</param-name>
<param-value>poseidon-settlement-service</param-value>
</context-param>

<!--由Spring载入的Log4j配置文件位置 -->
<context-param>
<param-name>log4jConfiguration</param-name>
<param-value>classpath:log4j2.xml</param-value>
</context-param>

<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>

<!--Spring默认刷新Log4j配置文件的间隔,单位为毫秒 -->
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>60000</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

同时引入log4j-web包,启动项目,即可。