log4j看上去像是一种简单的,易配置的日志打印技术。但是实际使用的时候发现,还有各种很相似的日志技术。很多地方的配置一乱就不知道怎么对应了。所以应该把log4j的一切做个简单的分类记录。

(一)java.util.logging.Logger

这个在java的util包里面,不需要任何Maven依赖,是最最基本的日志打印。功能很不完善,基本没有人在大型软件中使用这个。

示例代码:(日志重要级从上往下递增)

 public class TestLog {
public static void main(String[] args) {
Logger logger = Logger.getLogger(TestLog.class.toString());
logger.log(Level.ALL, "ALL");
logger.log(Level.FINEST, "FINEST");
logger.log(Level.FINER, "FINER");
logger.log(Level.FINE, "FINE");
logger.log(Level.CONFIG, "CONFIG");
logger.log(Level.INFO, "INFO");
logger.log(Level.WARNING, "WARNING");
logger.log(Level.SEVERE, "SEVERE");
logger.log(Level.OFF, "OFF");
}
}

控制台输出:

一月 17, 2017 2:36:23 下午 com.jd.Test.TestLog main
信息: INFO
一月 17, 2017 2:36:23 下午 com.jd.Test.TestLog main
警告: WARNING
一月 17, 2017 2:36:23 下午 com.jd.Test.TestLog main
严重: SEVERE
一月 17, 2017 2:36:23 下午 com.jd.Test.TestLog main
禁用: OFF

那么为什么从info开始输出呢?原因是在很多实际应用中会只在控制台打印某个级别以上的日志信息。否则在调试的时候有可能会面对海量的杂乱日志而不知如何下手。所以这里也用了这样的设计。

想要修改默认的日志输出级别的话,去jre安装目录的lib下面:

 # Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO

那么,如何制定日志的输出目录呢?这个可以用java代码指定:

 public class TestLog {
public static void main(String[] args) throws SecurityException, IOException {
Logger logger = Logger.getLogger(TestLog.class.toString()); FileHandler fileHandler = new FileHandler("/Users/Davie/File/foo.log");
fileHandler.setLevel(Level.ALL);
logger.addHandler(fileHandler); logger.log(Level.ALL, "ALL");
logger.log(Level.FINEST, "FINEST");
logger.log(Level.FINER, "FINER");
logger.log(Level.FINE, "FINE");
logger.log(Level.CONFIG, "CONFIG");
logger.log(Level.INFO, "INFO");
logger.log(Level.WARNING, "WARNING");
logger.log(Level.SEVERE, "SEVERE");
logger.log(Level.OFF, "OFF");
}
}

但是结果是一个xml,比较难debug。

(二)common-logging

首先,它是apache提供的一个通用的日志接口,它的存在的意义是把普遍意义上的日志和真正的日志实现解耦合。那么它本身需要什么配置呢?其实什么都不需要。因为在运行时,它会自动去找实现。这里的实现可能是上文的java.util.logging.Logger这个JDK自带的最基本的日志实现,也可能是使用广泛的Log4j。

首先在Maven加入依赖:

 <dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>

然后在java类中写入:

 class Foo {

     private Log log = LogFactory.getLog(TestLog.class);

     public void method() {
// log.debug("debug");
log.info("info");
log.warn("warn");
log.error("error");
// log.fatal("fatal");
}
}

注释的这两行去掉注释是不起作用的。原因是目前只在Maven中添加了common-logging,它会使用JDK的实现,而JDK的实现是没有debug和fatal的。那么问题是到底这个common-logging会使用哪一个Log呢?它会按照如下规则寻找其实现类(转载自:http://jiangzhengjun.iteye.com/blog/520733):

1) 首先在classpath下寻找自己的配置文件commons-logging.properties,如果找到,则使用其中定义的Log实现类;

2) 如果找不到commons-logging.properties文件,则在查找是否已定义系统环境变量org.apache.commons.logging.Log,找到则使用其定义的Log实现类;

如果在Tomact中可以建立一个叫 :CATALINA_OPTS 的环境变量 
给 他的 值 : - Dorg.apache.commons.logging.Log = org.apache.commons.logging.impl.SimpleLog - Dorg.apache.commons.logging.simplelog.defaultlog = warn

3) 否则,查看classpath中是否有Log4j的包,如果发现,则自动使用Log4j作为日志实现类;

4) 否则,使用JDK自身的日志实现类(JDK1.4以后才有日志实现类);

5) 否则,使用commons-logging自己提供的一个简单的日志实现类SimpleLog;

这样呢,往往就可以保证按照用户的意愿实现log。

另外,以上的Maven依赖中如果加入了log4j相关的的依赖,反倒会在运行时报错,原因是common-logging发现了log4j,所以会使用log4j进行输出,但是这个时候log4j还没有配置于是会输出:

log4j:WARN No appenders could be found for logger (com.jd.logTest.Foo).

log4j:WARN Please initialize the log4j system properly.

log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

(三)SLF4j

这个其实也是一种接口。它自己不实现任何真正的日志打印,而是通过再引入别的包来转换。它的最特别的好处有两个,第一是可以通过占位符来打印日志,第二是配置具体实现简单且解耦合。

对于第一点,在日志系统的使用中,往往不只是要打印出:“**异常”,还要打印出具体的异常,和异常产生时的参数环境。最简单的例子如下:

     public void say(String word) {
if (word == null) {
log.error("输入参数异常,word:" + word);
return ;
}
System.out.println(word);
}

这样的问题是,当要打印的参数多时,要产生多个String对象。其次是代码很不直观。

对于第二点,如果没有使用这个接口,而是直接耦合了log4j。那么如果你在代码中引入了一个模块,而这个模块使用了别的日志系统,那么你就必须在你的代码里面引入那一个日志系统并且配置好,维护好。这很明显引入了不必要的工作。

那么你要怎么才能使用一个SLF4j呢?官网上有一张图:

这张图体现了SLF4j的核心思想:你只需要把正确的包引入你的工程,日志系统就能正常的工作。

那么具体如何使用SLF4j呢?首先,你需要一个最基本的SLF4j的API包:

 <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>

这个包提供了SLF4j的基本API。接着,你需要一个“转换的包”。说起转换,当然就能联想到适配器设计模式。这个地方可以理解为一个适配器。这个地方的理解是指宏观的理解:你引入了如果所示的对应的包,就把SLF4j对应的实现包引入,SLF4j就会自动去找这个包的实现。

同时这个地方记录一下SLF4j和common-logging的实现方式的不同:

common-logging是通过动态查找的机制,在程序运行时自动找出真正使用的日志库。

SLF4j在编译时静态绑定真正的Log库。

(四)log4j

这个可以说是最享有大名的一个日志系统。

说起这个日志系统,里面的坑其实不少。我一次上线测试的时候,出了bug。但是比出bug更可怕的是:本地是可以打出日志的,而在线上的服务器就无法打出日志。那个错误直接导致我无法正常debug。

要使用这个日志工具,首先添加依赖:

         <dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>

然后再添加配置文件。这里的具体的配置有用log4j.xml和log4j.properties两种方式,这里使用xml来配置。把xml加入编译路径中:src/main/resources:

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'> <appender name="ALL" class="org.apache.log4j.DailyRollingFileAppender">
<param name="file" value="/Users/Davie/File/export/Logs/TestProject/ALL.log" />
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d [%7r] %6p - %30.30c - %m \n" />
</layout>
</appender> <appender name="FILE_MONITORFOLDER" class="org.apache.log4j.DailyRollingFileAppender">
<param name="file"
value="/Users/Davie/File/export/Logs/TestProject/FILE_MONITORFOLDER.log" />
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d [%7r] %6p - %30.30c - %m \n" />
</layout>
</appender> <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d [%7r] %6p - %30.30c - %m \n" />
</layout>
</appender> <logger name="FILE_MONITORFOLDER" additivity="false">
<level value="${task.log.level}" />
<appender-ref ref="FILE_MONITORFOLDER" />
</logger> <logger name="CONSOLE" additivity="false">
<level value="${task.log.level}" />
<appender-ref ref="CONSOLE" />
</logger> <root>
<priority value="${task.log.level}" />
<appender-ref ref="ALL" />
${log4j.log.appender.console}
</root>
</log4j:configuration>

在实际的使用中,往往由一些意想不到的点。

比如说,在dev时(可以理解为Profile处于dev),我们应当允许其不单单向日志文件写日志,还可以同时向控制台打印日志。这样在工程师调试的时候可以说是极其方便的。但是一旦到了线上的环境,恐怕就没有控制台这个东西了,日志全部写入到了日志文件,一般是通过SHELL语句去跟踪log日志文件的打印情况来实现的。这也就是为什么我一直没找到我的日志。在上面的配置文件中,最后的:${log4j.log.appender.console}到底是什么呢?实际上,在log4j中,如果你没有配置<logger>或者<category>这种标签,日志就会去找<root>来实现打印。那么<root>这里的这句话其实是一句控制是否往控制台打印日志的语句。

在dev的profile中,配置是:

 <log4j.log.appender.console><![CDATA[<appender-ref ref="CONSOLE"/>]]></log4j.log.appender.console>

在其他的profile中,配置是:

 <log4j.log.appender.console></log4j.log.appender.console>

当然也就是不往控制台打印日志的意思了。那么当时我的日志没有打印出来的逻辑也就是:

1,使用了:private Logger log = Logger.getLogger(Foo.class);

2,配置文件里面没有配置这个logger,于是自动去找<root>,

3,本地的dev的profile配置了要打印到控制台,于是正常,而线上的test profile则根本没有配置打印到控制台(当然实际上打印了也是没有意义的,单纯浪费效率罢了)。

然后,往往在调试的时候会有很多调试时的日志,也就是一些局部的过程,用:log.debug打印出来。很明显,如果任由这种日志挤成一大堆,那么首先是性能的开销,其次是一旦出了问题,在浩如烟海的杂乱日志中恐怕要很费力气才能得到自己想要的信息。所以在不同的Maven的Profile中,一般会配置一个不同的量来控制当前环境下的日志打印级别。比如上文中的:${task.log.level}

在dev的profile中:

 <task.log.level>DEBUG</task.log.level>

在test的profile中:

 <task.log.level>INFO</task.log.level>

那么这样就可以很细节的控制打印日志的颗粒度。

第三个问题是这样的,上文已经说过形如:

 private Logger log = Logger.getLogger(“foo”);

这样写,结果是你配置了foo这个logger,就会去找这个logger,否则会去root里面。那么在一个多module的工程中,就不能像写小玩意一样随便选择当前的类作为日志了。一般在这样的大工程中,都会为某个module,或者某个模块写一个logger:

     <logger name="FILE_MONITORFOLDER" additivity="false">
<level value="${task.log.level}" />
<appender-ref ref="FILE_MONITORFOLDER" />
</logger>
<appender name="FILE_MONITORFOLDER" class="org.apache.log4j.DailyRollingFileAppender">
<param name="file"
value="/Users/Davie/File/export/Logs/TestProject/FILE_MONITORFOLDER.log" />
<param name="DatePattern" value="'.'yyyy-MM-dd" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d [%7r] %6p - %30.30c - %m \n" />
</layout>
</appender>

这样在MONITORFOLDER这个模块中,就这么用:

 private Logger log = Logger.getLogger(“FILE_MONITORFOLDER”);

这样,在线上的时候,就可以去对应的日志文件中看这个模块独有的日志信息了。

以上都是描述这些东西单独怎么用,但实际上很多时候会结合这些东西来用。

比如你想用SLF4J+log4j,那么Maven里面你这么配置:

         <dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>

JAVA代码代码里面这么写:

 private Logger log = LoggerFactory.getLogger("FILE_MONITORFOLDER");

那么实际运行的时候,效果和上面直接使用log4j一样,因为SLF4J通过依赖包找到了其底层实现是log4j。

如果情况更极端一点:你最开始的代码使用了common-logging,那么你需要先把和这个转化为SLF4J,再按照SLF4J的规则打印日志。那么是否意味着你要改动代码?不需要,你只需要添加一个依赖:

         <dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>

这样的话,common-logging就会通过SLF4J去打印日志了。

log4j的各种类的配置的更多相关文章

  1. log4j.properties 详解与配置步骤(转)

    找的文章,供参考使用 转自 log4j.properties 详解与配置步骤 一.log4j.properties 的使用详解 1.输出级别的种类 ERROR.WARN.INFO.DEBUGERROR ...

  2. springmvc+log4j操作日志记录,详细配置

    没有接触过的,先了解一下:log4j教程 部分内容来:log4j教程 感谢! 需要导入包: log包:log4j-12.17.jar 第一步:web.xml配置 <!-- log4j配置,文件路 ...

  3. 转!数据库连接池概念、种类、配置(DBCP\C3P0\JndI与Tomact配置连接池)

    数据库连接池概念.种类.配置(DBCP\C3P0\JndI与Tomact配置连接池) 一.DBCP 连接:DBCP 连接池是 Apache 软件基金组织下的一个开源连接池实现. 需要的 java 包c ...

  4. log4j.properties 详解与配置步骤

    一.log4j.properties 的使用详解 1.输出级别的种类 ERROR.WARN.INFO.DEBUGERROR 为严重错误 主要是程序的错误WARN 为一般警告,比如session丢失IN ...

  5. 【配置】log4j.properties 详解与配置步骤

    一.Log4j基本使用方法 Log4j由三个重要的组件构成:[日志信息的优先级],[日志信息的输出目的地],[日志信息的输出格式]. 日志信息的优先级从高到低有ERROR.WARN. INFO.DEB ...

  6. 转--log4j.properties 详解与配置步骤

    一.log4j.properties 的使用详解 1.输出级别的种类 ERROR.WARN.INFO.DEBUGERROR 为严重错误 主要是程序的错误WARN 为一般警告,比如session丢失IN ...

  7. log4j.properties 详解与配置步骤总结

    先提供一个项目中使用log4j.properties配置 #log4j.rootLogger=WARN, stdout, file log4j.rootLogger=INFO,console,dail ...

  8. 【转】log4j.properties 详解与配置步骤 - edward0830ly的专栏 - 博客频道 - CSDN.NET

    一.log4j.properties 的使用详解 1.输出级别的种类 ERROR.WARN.INFO.DEBUGERROR 为严重错误 主要是程序的错误WARN 为一般警告,比如session丢失IN ...

  9. 从零开始学 Java - log4j 项目中的详细配置

    你还会用笔来写字么 我是不怎么会了,有时候老是拿起笔之后不知道这个字怎么写,这时候就会拿起手机去打出来:有时候还会写出来这个字之后越看越不像,这时候就开始怀疑自己的能力了:有时候写出来了一大堆字之后, ...

随机推荐

  1. Inventory Update-freecodecamp算法题目

    Inventory Update 1.要求 依照一个存着新进货物的二维数组,更新存着现有库存(在 arr1 中)的二维数组. 如果货物已存在则更新数量 . 如果没有对应货物则把其加入到数组中,更新最新 ...

  2. DB设计工具——dbschema

      Preface       I've got a db design job about meeting room booking system last week.There're many s ...

  3. ethereum(以太坊)(二)--合约中属性和行为的访问权限

    pragma solidity ^0.4.0; contract Test{ /* 属性的访问权限 priveta public internal defualt internal interlnal ...

  4. js中break跳出多层循环

    // 当执行多重循环的时候break的情况 outer: for(var i=0;i<10;i++){ inter: for(var j=0;j<10;j++){ if(i>5){ ...

  5. HTML+CSS : 笔记整理(2 常规流,BFC,固定定位,z-index)

    BFC和常规流的关系是什么:常规流遵循BFC,IFC规则. 定位规则总体来说三种: 常规流,浮动,绝对定位(CSS3里面新加了一种flex) 其中常规流包括BFC,IFC等规则,块级元素一个一排地从上 ...

  6. windows下使用curl.exe模拟ajax请求

    curl 是一般linux发行版中都带有的小工具,利用这个工具可以很方便的下载文件,我一般使用这个工具来查看某个页面相应的HTTP头信息,在Windows系统中我们也一样可以使用这个工具,如果不需要支 ...

  7. jquery 配合 ajax 完成 在线编辑 你值得拥有

    思路分析: 将 table中的表格 改变成为 input表格框获得值 ajax配合修改 删除 <?php use yii\helpers\Url; $web = Url::base(); ?&g ...

  8. flask项目实战--论坛

    项目结构搭建 1:用pycharm创建flask bbs项目 2:分别创建config.py.exts.py.models.py.manage.py文件 创建一个apps包存放前台,后台,公共的模块 ...

  9. 爬虫之urllib模块

    1. urllib模块介绍 python自带的一个基于爬虫的模块. 作用:可以使用代码模拟浏览器发起请求. 经常使用到的子模块:request,parse. 使用流程: 指定URL. 针对指定的URL ...

  10. 交互式的Bourne shell

    简介 当以交互的方式使用命令行时,shell有一些特殊的内置变量,这些变量中包含一系列选项.如果在选项中包含字母i,则表示shell以交互方式运行. # case "$-" in ...