目录

log4j

概述

是什么

log4j: log for java

一个开源的、轻量级的、用于日志管理的组件

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

Log4j由三个重要的组件构成:日志信息的优先级,日志信息的输出目的地,日志信息的输出格式。日志信息的优先级从高到低有ERROR、WARN、 INFO、DEBUG,分别用来指定这条日志信息的重要程度;日志信息的输出目的地指定了日志将打印到控制台还是文件中;而输出格式则控制了日志信息的显示内容。

log4j和self4j、logback的联系与区别

self4j(Simple logging Facade for Java)意思为简单日志门面,它是把不同的日志系统的实现进行了具体的抽象化,只提供了统一的日志使用接口,使用时只需要按照其提供的接口方法进行调用即可,由于它只是一个接口,并不是一个具体的可以直接单独使用的日志框架,所以最终日志的格式、记录级别、输出方式等都要通过接口绑定的具体的日志系统来实现,这些具体的日志系统就有log4j,logback,java.util.logging等,它们才实现了具体的日志系统的功能。

举个例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
假设日志系统A的info日志输出方法如下
A.message();
日志系统B的info日志输出方法如下
B.show();
log4j的info日志输出方法如下
logger.getInfo()     //演示用  实际不是这个方法

slf4j的实现就是
slf4j slf4j = new slf4j (A);
log.info();
这样我们用的就是日志系统A的方法

现在假设我们的系统所在环境仅仅有log4j的日志输出环境那么原来系统仅仅需要配置新的日志源即可
slf4j slf4j = new slf4j (log4j);

而不需要更改其他代码

Logback是由log4j创始人设计的另一个开源日志组件,官方网站: http://logback.qos.ch。Logback相比log4j有诸多优势,足以取代log4j。logback和log4j比较,前者是后者改良,logback配置详解

能干嘛

  • 日志监控打印,在项目试运行期需要记录用户所有的操作
  • 添加新的内容,比如时间和线程
  • 程序调试期间,记录运行的步骤和运行行
  • 成功上线稳定运行后,不再需要打印了
  • 多个日志的输出源,比如到数据库、eclipse控制台,或者日志文件到linux服务器下

去哪下

apache log工具

maven依赖

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

怎么用

log4j.properties介绍

  • 四个关键

    • 目的地:appender

      所有的输出源至少有两个:控制台/磁盘,这是工程经验

    • 布局:layout

    • 控制单元:logger

    • 级别 leveal

  • # 表示注释

  • 逐行解释

一句话总结就是配置 以什么什么样的格式,按照什么样的日志优先级,将日志输出到什么地方。

配置Demo

 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
# appender名称=类型 eh 公司名,appender名称 eh.file
log4j.appender.eh.File=org.apache.log4j.DailyRollingFileAppender
# 文件名,也是当日文件名
log4j.appender.eh.File.file=/tmp/com.eh/java8.log
# 往日文件名后缀
log4j.appender.eh.File.DatePattern=.yyyy-MM-dd
# 输出样式布局,PatternLayout->用户自定义
log4j.appender.eh.File.layout=org.apache.log4j.PatternLayout
# 具体转换样式
# %d 日期
# %5p 5位对齐,eg INFO 4位,不够5位在前面补空格, p 表示优先级 priority
# %C 包名+类名
# %M 方法
# %m message
# %n 回车
log4j.appender.eh.File.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p (%C:%M) - %m%n

# 控制台
log4j.appender.eh.Console=org.apache.log4j.ConsoleAppender
log4j.appender.eh.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.eh.Console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p (%C:%M) - %m%n

# 默认控制单元,如果没有配置logger的话就使用这个
# 级别,appender...
log4j.rootLogger=warn,eh.File,eh.Console

日志级别和获得

Level

Log4j有多种级别:OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL。

Log4j建议只用四个级别分别是ERROR、WARN、INFO、DEBUG。

他们四个的级别优先级:debug<info<warn<error,假如你选择的级别是info,那就是往后靠,打印出大于等于info级别的全部标注日志

优先级与输出关系:打印自身往后靠

使用Demo

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.eh.eden.log4jtest;
import org.apache.log4j.Logger;

public class Log4jTest {
    private static final Logger logger = Logger.getLogger(Log4jTest.class);

    public static void main(String[] args) {
        logger.debug("debug log4j");
        logger.info("info log4j");
        logger.warn("warn log4j");
        logger.error("error log4j");
    }
}

输出源Appender

常见Appnder,前3个常用

  • org.apache.log4j.ConsoleAppender(控制台)
  • org.apache.log4j.FileAppender(就一个文件)
  • org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
  • org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
  • org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
  • org.apache.log4j.jdbc.JDBCAppender(把日志用JDBC记录到数据库中)

布局LayOut

布局就是指输出信息的格式。在Log4j中称作Layout

常见Layout

  • org.apache.log4j.HTMLLayout(以HTML表格形式布局),
  • org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
  • org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
  • org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

常用PatternLayout介绍

配合自定义布局(org.apache.log4j.PatternLayout)使用

常用的patternLayout介绍

  • %m 输出代码中指定的消息

  • %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL

    %5p表示 5位对齐,eg INFO 4位,不够5位在前面补空格

  • %r 输出自应用启动到输出该log信息耗费的毫秒数

  • %c 输出所属的类目,通常就是所在类的全名

  • %t 输出产生该日志事件的线程名

  • %n 输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n”

  • %d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy-MM-dd HH:mm:ss,SSS},输出类似:2015-12-20 18:35:51,768

logger控制单元

多个控制单元并存时遵循的规则

  • 级别取精确,取类的路径越全的那个控制单元的级别
  • 输出为各自,几个控制单元按照确定好的级别各自输出

举个例子

1
2
3
4
5
# 默认控制单元,如果没有配置logger的话就使用这个
# 级别,appender...
log4j.rootLogger=info,eh.File,eh.Console
# 按照包名控制
log4j.logger.com.eh.eden.log4jtest=error,eh.Console
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.eh.eden.log4jtest;
import org.apache.log4j.Logger;

public class Log4jTest {
    private static final Logger logger = Logger.getLogger(Log4jTest.class);

    public static void main(String[] args) {
        logger.debug("debug log4j");
        logger.info("info log4j");
        logger.warn("warn log4j");
        logger.error("error log4j");
    }
}

按照上述规则,级别取ERROR,两个控制单元各自输出,所以控制台会有两条error日志,文件里面有一条error日志。

Case

实际使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Log4jTest {
    private static final Logger logger = Logger.getLogger(Log4jTest.class);

    public static void main(String[] args) {
        int arg = 0;
        logger.info("arg=" + arg);
        Integer result = null;
        try {
            result = 10 / arg;
        } catch (Exception e) {
            logger.error("m不能为0", e);
        } finally {
            logger.info("result=" + result);
        }
    }
}
// 输出
2020-10-03 14:06:55,765  INFO (com.eh.eden.log4jtest.Log4jTest:main) - arg=0
2020-10-03 14:06:55,780 ERROR (com.eh.eden.log4jtest.Log4jTest:main) - m不能为0
java.lang.ArithmeticException: / by zero
	at com.eh.eden.log4jtest.Log4jTest.main(Log4jTest.java:20)
2020-10-03 14:06:55,801  INFO (com.eh.eden.log4jtest.Log4jTest:main) - result=null

log4j.properties总结

Appender、Layout、Logger三者之间的关系

  1. 每个appender后面必然需要跟随layout,指定自己的风格样式
  2. 每个Logger都可以指定一个级别,同时引用一个或多个Appender

log4j.xml

log4j中log4j.properties和log4j.xml的加载顺序

log4j启动时,默认会寻找source folder下的log4j.xml配置文件,若没有,会寻找log4j.properties文件。

配置介绍

 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
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM
        "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">

<log4j:configuration>
    <appender name="eh.console" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{HH:mm:ss,SSS} %5p (%C:%M) - %m%n"/>
        </layout>
        <!--
            log4j中常用的Filter分为四种:DenyAllFilter、LevelMatchFilter、LevelRangeFilter、StringMatchFilter
            这里需要注意的是,一旦匹配了某个filter,就无法再匹配后续的filter了

            LevelRangeFilter:控制输出源输出哪个级别的日志
            AcceptOnMatch:用来控制匹配到的appender是否打印日志
            不打印指定level的日志:
            <filter class="org.apache.log4j.varia.LevelRangeFilter">
               <param name="LevelToMatch" value="ERROR" />
               <param name="AcceptOnMatch" value="false" />
            </filter>
         -->
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="levelMin" value="info"/>
            <param name="levelMax" value="info"/>
            <param name="AcceptOnMatch" value="true"/>
        </filter>
    </appender>

    <appender name="eh.file" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="File" value="/tmp/com.eh/java8xml.log"/>
        <param name="Append" value="true"/>
        <param name="DatePattern" value="'.'yyyy-MM-dd"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{HH:mm:ss,SSS} %5p (%C{1}:%M) - %m%n"/>
        </layout>
    </appender>
    <!--
        additivity=false,停止日志传播机制,只要日志传到当前logger,日志就不会再往后传播
    -->
    <logger name="com.eh.eden" additivity="false">
        <level value="info"/>
        <appender-ref ref="eh.console"/>
        <appender-ref ref="eh.file"/>
    </logger>

    <root>
        <level value="error"/>
        <appender-ref ref="eh.file"/>
        <appender-ref ref="eh.console"/>
    </root>
</log4j:configuration>