log4j的简单介绍

Posted fan2012huan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了log4j的简单介绍相关的知识,希望对你有一定的参考价值。

待完善:
(1).这里有一篇介绍log4j较为详细的博文,粗略看了下,下次再深入看看,写一个较为深入的介绍,先把连接记录在这里:
log4j详细介绍
关于log4j的日志一直弄的不清不楚。这里总结总结:
(2)log4j有很多的bug,如死锁等。现在出现了logbacklog4j2。下次再介绍。

1. Demo示例

在工程中经常看到类似这样的代码:

private static final Log logger = LogFactory.getLog(XxxxClass.class);
private static final Logger logger = Logger.getLogger(ClassA.class.getName()); 

至于上面两种获得logger方式的异同后面再说。这里先说说,如何找到这个日志的位置。
先看看XxxxClass.class以及XxxxClass.class.getName()在java中的输出:

package org.fan.learn.log;
/**
 * Created by fan on 16-6-14.
 */
public class XxxxClass {
    public static void main(String[] args) {
        System.out.println(XxxxClass.class);
        System.out.println(XxxxClass.class.getName());
    }
}

上面的输出如下所示:
class org.fan.learn.log.XxxxClass
org.fan.learn.log.XxxxClass
第一中输出比第二种输出多了一个class。
那么下面两句getLog时有差异吗?没有任何差异。

private static final Logger logger = Logger.getLogger(XxxxClass.class);
private static final Logger logger = Logger.getLogger(XxxxClass.class.getName());

Logger.getLogger有重载的方法,查看源代码如下:

public static Logger getLogger(String name) {
        return LogManager.getLogger(name);
    }
    public static Logger getLogger(Class clazz) {
        return LogManager.getLogger(clazz.getName());
    }

可见Class参数的getLogger最终会调用String参数的getLogger。
请看具体例子:
工程结构如下:
这里写图片描述
pom文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.fan.learn.log</groupId>
    <artifactId>log</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>
</project>

src代码:

package org.fan.learn.log;
import org.apache.log4j.Logger;
/**
 * Created by fan on 16-6-14.
 */
public class XxxxClass {
    private static final Logger logger = Logger.getLogger(XxxxClass.class);
    //private static final Logger logger = Logger.getLogger(XxxxClass.class.getName());
    private static final Logger otherLogger = Logger.getLogger("other");
    public static void main(String[] args) {
        System.out.println(XxxxClass.class);
        System.out.println(XxxxClass.class.getName());
        logger.info("log test");
        otherLogger.info("other log test");
    }
}

log4j.xml的配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

 <appender name="demoLoggerAppender" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="File" value="/home/fan/logs/demo/demo.log" />
        <param name="DatePattern" value="'.'yyyy-MM-dd-HH" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{yyyy-MM-dd-HH:mm:ss,SSS} %5p - method:%l%m%n" />
        </layout>
    </appender> 

    <appender name="rootLoggerAppender" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="File" value="/home/fan/logs/demo/stdout.log" />
        <param name="DatePattern" value="'.'yyyy-MM-dd-HH" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{yyyy-MM-dd-HH:mm:ss,SSS} %5p - method:%l%m%n" />
        </layout>
    </appender>
    <logger name="org.fan.learn" additivity="false">
        <level value="debug" />
        <appender-ref ref="demoLoggerAppender" />
    </logger>
    <root>
        <level value="info" />
        <appender-ref ref="rootLoggerAppender" />
    </root>
</log4j:configuration>

看下日志输出:
这里写图片描述
在代码中getLogger(XxxxClass.class);应该在log4j.xml中匹配org.fan.learn.log.XxxxClass,但是在log4j.xml中没有这个logger标签,但是却在日志中打印出来了。这是因为log4j有一个与java类似的继承体系。比方说,如果要得到org.fan.learn.log.XxxxClass的logger,但是在log4j中没有配置,则会找org.fan.learn.log的logger,如果没有找到org.fan.learn.log的logger,则会找org.fan.learn的logger。这时找到了就使用他的配置。
像getLogger(“other”)在log4j中根本就不存在,就会找到root的logger标签,并使用root的配置。就像上面的输出结果一样。

注意到:<logger name="org.fan.learn" additivity="false"> 有一个additivity=false。如果没有这个开关,则会继续将日志传递给root标签,这样在stdout.log中也会有相同的日志输出。在开发中一般将这个置为false。
update-20160219-18:00
注意:Logger的level也有继承关系。比如,如果org.fan.learn Logger没有定义level,则它会继承root Logger的level。

2.log4j的appender类型

log4j提供的appender类型主要有下面5种:
org.apache.log4j.ConsoleAppender(控制台)
org.apache.log4j.FileAppender(文件)
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生新文件)
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

其中常用的只有两种:DailyRolllingFileAppenderRollingFileAppender。下面先简单介绍其余两种然后再重点介绍这两种。
(1)ConsoleAppender
就是讲日志直接打印在控制台上。这个没什么用处,要打印在控制台上,直接使用Java的输出语句好了。
(2)FileAppender
这个只是简单的将日志写到文件中。其实就相当于将控制台上的输出重定向到一个文件中。
(3)WriterAppender
这个没怎么接触过,等用到再来这里补充
(4)DailyRollingFileAppender
这个Appender用的比较多,这个可以根据该类的一个属性DatePattern,来设置是一天产生一个日志文件,还是一小时产生一个日志文件。
在DailyRollingFileAppender中可以指定monthly(每月)、weekly(每周)、daily(每天)、half-daily(每半天)、hourly(每小时)和minutely(每分钟)六个频度,这是通过为 DatePattern选项赋予不同的值来完成的。DatePattern选项的有效值为:
具体支持的事件有如下几种:

yyyy-MM,对应monthly(每月)
yyyy-ww,对应weekly(每周)
yyyy-MM-dd,对应daily(每天)
yyyy-MM-dd-a,对应half-daily(每半天)
yyyy-MM-dd-HH,对应hourly(每小时)
yyyy-MM-dd-HH-mm,对应minutely(每分钟)
常见的就是每天和每小时。就像上面配置就是每小时产生一个日志文件:

<param name="DatePattern" value="'.'yyyy-MM-dd-HH" />

注意:DatePattern中不用处理的文字要放到单引号(”)中,如上面的(.)。
请看上面例子每小时产生的日志样式:
这里写图片描述
16点之后,log4j就会对demo.log与stdout.log进行重命名。
(5)RollingFileAppender
文件大小到达指定尺寸的时候产生新文件。
有的时候日志文件有可能很大,这导致查看日志时很不方便。如果日志文件很大,而你使用vim方式打开了这个文件,那么会很危险。有可能会将服务器给搞垮,因为,这样打开会很耗费内存。这时可以将文件到达一定大小之后就生成新的文件。如500M之后就生成新的文件。
将上面的rootLoggerAppender配置成RollingFileAppender的形式:

<appender name="rootLoggerAppender" class="org.apache.log4j.RollingFileAppender">
        <param name="File" value="/home/fan/logs/demo/stdout.log" />
        <!--<param name="DatePattern" value="'.'yyyy-MM-dd-HH" />-->
        <param name="Encoding" value="utf-8" />
        <param name="MaxFileSize" value="1KB"/>
        <param name="MaxBackupIndex" value="2" />
        <param name="Append" value="true" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{yyyy-MM-dd-HH:mm:ss,SSS} %5p - method:%l%m%n" />
        </layout>
    </appender>

其中:
Encoding表示日志文件的编码方式,如果日志文件中的中文乱码很有可能跟这个有关系。而且需要更换新的日志文件时编码修改才会有效。
MaxFileSize表示日志文件的最大大小,需要说明的是:并不是文件大小到达1024B,就会将剩余的字节写入到另一个日志文件中,而是将这一次的日志全部写入到这个文件中,下次再写别的文件。
MaxBackupIndex表示日志文件后缀的最大编号。从1开始编号。
现在有个问题,如果日志文件已经写满了两个stdout.log了,而且新的stdout.log也满了,这是怎么办呢?
先看下面的两个已经写满的stdout.log文件:
这里写图片描述
这里写图片描述
再写满stdout.log看看:
这里写图片描述
这里写图片描述

由此可以看到:将原来的stdout.log.1更名为stdou.log.2,将新的stdout.log更名为stdout.log.1,这其实时做的一个循环更名,依次替换。

3.log4j的日志信息格式化

org.apache.log4j.htmlLayout(以HTML表格形式布局)
org.apache.log4j.PatternLayout(可以灵活地指定布局模式,最常用)
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
下面只介绍 PatternLayout方式,这个用的最多。

<layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{yyyy-MM-dd-HH:mm:ss,SSS} %5p - method:%l%m%n" />
        </layout>

log4j采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下:
%m 输出代码中指定的消息
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL   
%r 输出自应用启动到输出该log信息耗费的毫秒数
%c 输出所属的类目,通常就是所在类的全名
%t 输出产生该日志事件的线程名
%n 输出一个回车换行符,Windows平台为“\\r\\n”,Unix平台为“\\n”
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似: 2002年10月18日 22:10:28,921
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)

4.log4j的日志优先级

log4j的日志优先级主要有5种:fatal,error,warn,info,debug(优先级依次降低)。即,如果设置成info,则只会打印fatal,error,warn,info的日志信息,而忽略debug日志信息。这方便开发时可以通过大量的debug日志来帮助调试,而到线上时将日志级别提升成info,减少线上不必要的debug日志打印。

5.LogFactory.getLog与Logger.getLogger的区别

LogFactory是Common Logging的一个基类。Common Logging提供的是一个日志接口,它不依赖于具体的实现方式。也就是说,它与底层的日志实现方式独立的。你既可以使用log4j作为日志具体实现,也可以使用JDK中自带的日志实现,使得系统工程可以根据具体需求随意改变日志实现工具。
Common Logging支持的日志实现包括:Jdk14Logger,Log4JLogger,LogKitLogger,NoOpLogger (直接丢弃所有日志信息),还有一个SimpleLog。在工程中常用的也就是log4j。这么多的实现,Common Logging到底使用哪种实现呢?它是按照下面的方式进行选择的:
(1)首先在CLASSPATH下查找commons-logging.properties文件,这个属性文件至少定义 org.apache.commons.logging.Log属性,它的值应该是上述任意Log接口实现的完整限定名称。如果找到 org.apache.commons.logging.Log属相,则使用该属相对应的日志组件。结束发现过程。
(2).如果上面的步骤失败(文件不存在或属相不存在),common-logging接着检查系统属性 org.apache.commons.logging.Log。如果找到org.apache.commons.logging.Log系统属性,则使 用该系统属性对应的日志组件。结束发现过程。
(3).如果找不到org.apache.commons.logging.Log系统属性,common-logging接着在CLASSPATH中寻 找log4j的类。如果找到了就假定应用要使用的是log4j。不过这时log4j本身的属性仍要通过log4j.properties(或者log4j.xml)文件正确配置。结束发现过程。
(4).如果上述查找均不能找到适当的Logging API,但应用程序正运行在JRE 1.4或更高版本上,则默认使用JRE 1.4的日志记录功能。结束发现过程。
(5).最后,如果上述操作都失败(JRE 版本也低于1.4),则应用将使用内建的SimpleLog。SimpleLog把所有日志信息直接输出到System.err。结束发现过程。
但是在工程中基本上不在CLASSPATH中配置common-logging文件,也不在系统属性中配置common-logging文件,只是简单的将log4j的JAR包放在CLASSPATH,这样就实现了common-logging与log4j的融合。

以上是关于log4j的简单介绍的主要内容,如果未能解决你的问题,请参考以下文章

日志组件介绍(common-logging,log4j,slf4j,logback)

java日志组件介绍(common-logging,log4j,slf4j,logback )

slf4j的介绍

简单了解 SLF4J LOG4J Logback logging 日志框架

LogBack的使用介绍

log4j的简单介绍