Java服务_使用SLF4J&Log4j

Java服务_使用SLF4J&Log4j

1.log发展

1.1 JDK Logging

在编写程序过程中,常常使用system.out.println()来打印过程变量,测试代码逻辑是否正确。当代码出问题时又要删除这些system.out.println()语句,这样非常麻烦,替代方式就是使用日志。

java标准库内置了日志工具包java.util.logging,可以直接使用。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
import java.util.logging.Logger;

public class JDKLoggingTest {
private static final Logger logger = Logger.getGlobal();

public static void main(String[] args) {
logger.info("start process...");
logger.warning("memory is running out...");
logger.fine("ignored.");
logger.severe("process will be terminated...");
}
}

控制台输出:

1
2
3
4
5
6
五月 26, 2022 6:38:32 下午 com.jd.hbase.logtest.JDKLoggingTest main
信息: start process...
五月 26, 2022 6:38:32 下午 com.jd.hbase.logtest.JDKLoggingTest main
警告: memory is running out...
五月 26, 2022 6:38:32 下午 com.jd.hbase.logtest.JDKLoggingTest main
严重: process will be terminated...
  • 日志工具类可以自动打印时间、调用类、调用方法等很多有用信息。
  • 日志工具类可以设置输出级别,默认输出级别为info,info以下的输出级别不会被打印出来。但是要修改JDK Logging的输出级别等配置不太方便,需要在JVM启动时传递参数。

1.2 Commons Logging

Commons Logging是由apache创建的第三方日志工具,相比于JDK Logging最大的变化:

  • 提供了通过LogFactory获取Log类实例的日志接口;
  • 提供了重载方法,使得记录异常更加简单,如info(String, Throwable)。

但是Commons Logging只是提供了一些日志接口,还必须挂载其他底层日志系统。如果有Log4j包则使用Log4j作为日志底层,如果没有则使用JDK Logging作为日志底层。

使用前需要引入依赖:

1
2
3
4
5
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>

代码实例:

1
2
3
4
5
6
7
8
9
10
11
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class CommonsLoggingTest {
private static final Log log = LogFactory.getLog(CommonsLoggingTest.class);

public static void main(String[] args) {
log.info("start...");
log.warn("end...");
}
}

控制台输出:

1
2
3
4
[2022-05-26 21:04:13:199] [INFO ] [method:com.jd.hbase.logtest.CommonsLoggingTest.main(CommonsLoggingTest.java:10)]
start...
[2022-05-26 21:04:13:201] [WARN ] [method:com.jd.hbase.logtest.CommonsLoggingTest.main(CommonsLoggingTest.java:11)]
end...

1.3 Log4j

Log4j是一个用于替代JDK Logging的第三方日志底层实现框架,可以通过xml等配置文件实现组件化。

通过在配置文件中配置不同的appender可以把一条日志输出到不同的目的地,比如:

  • console:输出到控制台;
  • file:输出到文件;
  • socket:通过网络输出到远程计算机;
  • jdbc:输出到数据库。

使用Log4j需要将log4j2.xml配置文件放到classpath下。

1.4 SLF4J

SLF4J类似于Commons Logging,是一套日志接口。

SLF4J相比于Commons Logging最大的变化:

  • 提供了重载方法,可以传入带占位符的字符串,后面传入的变量自动替换占位符,使得字符串拼接更加自然,如logger.info("小红的得分是{}分。", 98)

1.5 Logback

Logback类似于Log4j,是一套日志底层框架。

使用Logback需要将logback.xml配置文件放到classpath下。

2.log4j配置文件配置详解

2.1三大组件

最常用的日志框架就是SLF4J+Log4j。

log4j的配置文件按照版本有log4j.xml和log4j2.xml两种,log4j2.xml在配置规则上进行了一些优化,但大体上的组件和逻辑是相同的。

  • Properties:用于定义变量,供文件内其他位置使用;
  • appenders:用于定义日志输出的目的地;
  • loggers:用于定义日志输出器。

所有的日志输出语句都是先找到loggers,然后每个logger都会绑定一个或多个appenders,将这些日志语句打印到对应的目的地。

2.2 Loggers节点

Loggers主要包含两种具体的日志输出器:Root和Logger。

Root节点主要有两个属性:

  • level:用于设置日志输出级别,低于该级别的日志不会被输出打印;
  • AppenderRef:用于指定日志输出到哪个appender,必须是本配置文件中定义好的appender的name值。

Logger比Root多了一个属性:

  • level:用于设置日志输出级别;
  • name:用来指定需要使用该Logger的类或者包的全路径名;
  • AppenderRef:用于指定日志输出到哪个appender。(如果没有指定该属性,就会默认继承Root的该属性;如果指定了,就会在该指定appender和Root的appender中都输出;添加additivity=”false”属性,则表示只在自己指定的appender中输出。)

所有日志输出语句都是先找到Logger,判断是否是Logger所指定的类或者包,如果是则按照Logger定义的规则输出,如果不是则传递到Root按照Root定义的规则输出。

2.3 log4j.xml和log4j2.xml实例

log4j.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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
<!-- 将日志信息输出到控制台 -->
<appender name="myConsole" class="org.apache.log4j.ConsoleAppender">
<!-- 设置日志输出的样式 -->
<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>
<!--过滤器设置输出的级别,比logger中的level功能更高级,还可以设置最高输出级别。-->
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<!-- 设置日志输出的最小级别 -->
<param name="levelMin" value="DEBUG" />
<!-- 设置日志输出的最大级别 -->
<param name="levelMax" value="ERROR" />
<!-- 设置日志输出的xxx,默认是false -->
<param name="AcceptOnMatch" value="true" />
</filter>
</appender>

<!-- 将日志信息输出到文件,但是当文件的大小达到某个阈值的时候,日志文件会自动回滚 -->
<appender name="myFile" class="org.apache.log4j.RollingFileAppender">
<!-- 设置日志信息输出文件全路径名 -->
<param name="File" value="log/RollingFileAppender.log" />
<!-- 设置是否在重新启动服务时,在原有日志的基础添加新日志 -->
<param name="Append" value="true" />
<!-- 设置保存备份回滚日志的最大个数 -->
<param name="MaxBackupIndex" value="10" />
<!-- 设置当日志文件达到此阈值的时候自动回滚,单位可以是KB,MB,GB,默认单位是KB -->
<param name="MaxFileSize" value="10KB" />
<!-- 设置日志输出的样式 -->
<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="activexAppender" class="org.apache.log4j.DailyRollingFileAppender">
<!-- 设置日志信息输出文件全路径名 -->
<param name="File" value="log/DailyRollingFileAppender.log" />
<!-- 设置日志每分钟回滚一次,即产生一个新的日志文件 -->
<param name="DatePattern" value="'.'yyyy-MM-dd-HH-mm'.log'" />
<!-- 设置日志输出的样式 -->
<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>

<!-- 指定logger的设置,additivity指示是否遵循缺省的继承机制-->
<logger name="com.runway.bssp.activeXdemo" additivity="false">
<level class="org.apache.log4j.Level" value ="info"/>
<appender-ref ref="activexAppender" />
</logger>

<!-- 根logger的设置-->
<root>
<priority value ="debug"/>
<appender-ref ref="myConsole"/>
<appender-ref ref="myFile"/>
</root>
</log4j:configuration>

log4j2.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
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Properties>
<!-- 定义日志格式 -->
<Property name="log.pattern">%d{MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36}%n%msg%n%n</Property>
<!-- 定义文件名变量 -->
<Property name="file.err.filename">log/err.log</Property>
<Property name="file.err.pattern">log/err.%i.log.gz</Property>
</Properties>

<!-- 定义Appender,即日志输出目的地 -->
<Appenders>
<!-- 定义输出到屏幕 -->
<Console name="console" target="SYSTEM_OUT">
<!-- 日志格式引用上面定义的log.pattern -->
<PatternLayout pattern="${log.pattern}" />
</Console>

<!-- 定义输出到文件,文件名引用上面定义的file.err.filename -->
<RollingFile name="err" bufferedIO="true" fileName="${file.err.filename}" filePattern="${file.err.pattern}">
<PatternLayout pattern="${log.pattern}" />
<Policies>
<!-- 根据文件大小自动切割日志 -->
<SizeBasedTriggeringPolicy size="1 MB" />
</Policies>
<!-- 保留最近10份 -->
<DefaultRolloverStrategy max="10" />
</RollingFile>
</Appenders>

<Loggers>
<Root level="info">
<!-- 对info级别的日志,输出到console -->
<AppenderRef ref="console" level="info" />
<!-- 对error级别的日志,输出到err,即上面定义的RollingFile -->
<AppenderRef ref="err" level="error" />
</Root>
</Loggers>
</Configuration>

3.项目应用经验

3.1不同名配置文件加载顺序

log4j选择加载哪一个配置文件的优先级:

  • log4j-test.json或log4j-test.jsn
  • log4j-test.xml
  • log4j.json或log4j.jsn
  • log4j.xml

一般在新建项目编写代码时,会创建两个配置文件,log4j-test.xml和log4j.xml,两个配置文件中设置不同的日志输出级别。

比如log4j-test.xml中设置debug,log4j.xml中设置info,本地测试时使用log4j-test.xml可以看到详细的debug日志。正式上线时不要打包log4j-test.xml即可,避免日志文件冗长。

3.2 catalina.out

catalina.out用于记录tomcat的标准输出(stdout)和标准出错(stderr),这是在tomcat的启动脚本里指定的。

我们在tomcat运行项目里使用System.out打印的东西都会到这里来。如果我们在项目里使用其他的日志框架,配置了向Console输出的日志,也会打印到该文件中。比如以log4j为例,如果配置了org.apache.log4j.ConsoleAppender则会输出到catalina.out里。