slf4j的简单用法以及与log4j的区别

From: https://www.cnblogs.com/qlqwjy/p/9275415.html

之前在项目中用的日志记录器都是log4j的日志记录器,可是到了新公司发现都是slf4j,于是想着研究一下slf4j的用法。

   注意:每次引入Logger的时候注意引入的jar包,因为有Logger的包太多了。。。。。。

     Logger必须作为类的静态变量使用。原因如下:

1 使用static修饰的属性是归这个类使用的
2 也就是说不论这个类实例化多少个,大家用的都是同一个static属性
3 log4j记录的是当前类的日志,不是每个实例的日志
4 所以只要有一个记录就可以了创建日志记录器方法:(最好声明加final关键字)
    //private static final Logger logger = LoggerFactory.getLogger(Slf4jTest.class);// slf4j日志记录器private static final Logger logger = LoggerFactory.getLogger(Slf4jTest.class.getName());// slf4j日志记录器

 

简要记录一下日志级别:

复制代码

每个Logger都被了一个日志级别(log level),用来控制日志信息的输出。日志级别从高到低分为:
A:off         最高等级,用于关闭所有日志记录。
B:fatal       指出每个严重的错误事件将会导致应用程序的退出。
C:error      指出虽然发生错误事件,但仍然不影响系统的继续运行。
D:warm     表明会出现潜在的错误情形。
E:info         一般和在粗粒度级别上,强调应用程序的运行全程。
F:debug     一般用于细粒度级别上,对调试应用程序非常有帮助。
G:all           最低等级,用于打开所有日志记录。

复制代码

其实对于不同的版本有不同的级别,不过最常用的就是debug\info\warn\error.下面是摘自log4j-1.2.17.jar中的级别:
可以看出:all\TRACE\debug同级别。off与fatal同级别。

复制代码

package org.apache.log4j;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;public class Level extends Priority implements Serializable {/*** TRACE level integer value.* @since 1.2.12*/public static final int TRACE_INT = 5000;final static public Level OFF = new Level(OFF_INT, "OFF", 0);final static public Level FATAL = new Level(FATAL_INT, "FATAL", 0);final static public Level ERROR = new Level(ERROR_INT, "ERROR", 3);final static public Level WARN  = new Level(WARN_INT, "WARN",  4);final static public Level INFO  = new Level(INFO_INT, "INFO",  6);final static public Level DEBUG = new Level(DEBUG_INT, "DEBUG", 7);public static final Level TRACE = new Level(TRACE_INT, "TRACE", 7);final static public Level ALL = new Level(ALL_INT, "ALL", 7);static final long serialVersionUID = 3491141966387921974L;protectedLevel(int level, String levelStr, int syslogEquivalent) {super(level, levelStr, syslogEquivalent);}publicstaticLevel toLevel(int val, Level defaultLevel) {switch(val) {case ALL_INT: return ALL;case DEBUG_INT: return Level.DEBUG;case INFO_INT: return Level.INFO;case WARN_INT: return Level.WARN;case ERROR_INT: return Level.ERROR;case FATAL_INT: return Level.FATAL;case OFF_INT: return OFF;case TRACE_INT: return Level.TRACE;default: return defaultLevel;}}
。。。。。

复制代码

0.依赖的Jar包

复制代码

        <!-- slf4j 依赖包 --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.25</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.5</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.0-rc1</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.0-rc1</version></dependency>

复制代码

 

或者手动导入下面的包:

 

介绍:slf4j-api提供接口,slf4j-log4j提供具体的实现。也就是 slf4j只是一个日志标准,并不是日志系统的具体实现,如果项目只有slf4j的包是没有办法实现日志功能的。

 

1 基本介绍

  SLF4J不同于其他日志类库,与其它日志类库有很大的不同。SLF4J(Simple logging Facade for Java)不是一个真正的日志实现,而是一个抽象层( abstraction layer),它允许你在后台使用任意一个日志类库。如果是在编写供内外部都可以使用的API或者通用类库,那么你真不会希望使用你类库的客户端必须使用你选择的日志类库。

  如果一个项目已经使用了log4j,而你加载了一个类库,比方说 Apache Active MQ——它依赖于于另外一个日志类库logback,那么你就需要把它也加载进去。但如果Apache Active MQ使用了SLF4J,你可以继续使用你的日志类库而无需忍受加载和维护一个新的日志框架的痛苦。

  总的来说,SLF4J使你的代码独立于任意一个特定的日志API,这是对于API开发者的很好的思想。虽然抽象日志类库的思想已经不是新鲜的事物,而且Apache commons logging也已经在使用这种思想了,但SLF4J正迅速成为Java世界的日志标准。让我们再看几个使用SLF4J而不是log4j、logback或者java.util.logging的理由。

2 SLF4J对比Log4J,logback和java.util.Logging的优势

正如我之前说的,在你的代码中使用SLF4J写日志语句的主要出发点是使得你的程序独立于任何特定的日志类库,依赖于特定类库可能需要使用不同于你已有的配置,并且导致更多维护的麻烦。除此之外,还有一个SLF4J API的特性是使得我坚持使用SLF4J而抛弃我长期间钟爱的Log4j的理由,是被称为占位符(place holder),在代码中表示为“{}”的特性。占位符是一个非常类似于在Stringformat()方法中的%s,因为它会在运行时被某个提供的实际字符串所替换。这不仅降低了你代码中字符串连接次数,而且还节省了新建的String对象。通过使用SLF4J,你可以在运行时延迟字符串的建立,这意味着只有需要的String对象才被建立。而如果你已经使用log4j,那么你已经对于在if条件中使用debug语句这种变通方案十分熟悉了,但SLF4J的占位符就比这个好用得多。

3.slf4j的简单用法

1.简单使用

1.配置文件(log4j.properties)

复制代码

log4j.rootLogger=debug, Clog4j.appender.A=org.apache.log4j.ConsoleAppender
log4j.appender.A.layout=org.apache.log4j.PatternLayout
log4j.appender.A.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%nlog4j.appender.B=org.apache.log4j.FileAppender
log4j.appender.B.File=E:\\log.log
log4j.appender.B.layout=org.apache.log4j.SimpleLayoutlog4j.appender.C=org.apache.log4j.RollingFileAppender
log4j.appender.C.File=E:\\log.html
log4j.appender.C.MaxFileSize=1000KB
log4j.appender.C.MaxBackupIndex=10
log4j.appender.C.layout=org.apache.log4j.HTMLLayout
log4j.appender.C.encoding=gbklog4j.appender.D=org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File=E:\\log.log
log4j.appender.D.layout=org.apache.log4j.TTCCLayout

复制代码

 

注意:  

  1.如果需要输出到多个位置的时候可以逗号隔开,比如:   log4j.rootLogger=info, A, B 

  2.    log4j.appender.C.encoding=gbk 的配置是为了解决中文乱码,有时候也可以设置为UTF-8试试

 

关于pattern的设置如下:

复制代码

%p: 输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL,%d: 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy-MM-dd HH:mm:ss,SSS},输出类似:2011-10-18 22:10:28,921 
%r: 输出自应用启动到输出该log信息耗费的毫秒数 
%c: 输出日志信息所属的类目,通常就是所在类的全名 
%t: 输出产生该日志事件的线程名 
%l: 输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。 
%x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。 
%%: 输出一个"%"字符 
%F: 输出日志消息产生时所在的文件名称 
%L: 输出代码中的行号 
%m: 输出代码中指定的消息,产生的日志具体信息 
%n: 输出一个回车换行符,Windows平台为"\r\n",Unix平台为"\n"输出日志信息换行 

复制代码

 

2.测试代码:

复制代码

package cn.xm.exam.test;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Slf4jTest {private static Logger logger = LoggerFactory.getLogger(Slf4jTest.class);// slf4j日志记录器public static void main(String[] args) {// 普通的日志记录logger.debug("普通的日志记录");// {}占位符记录日志for (int i = 0; i < 3; i++) {logger.debug("这是第{}条记录", i);}// 用\转义{}logger.debug("Set \\{} differs from {}", "3"); // output:Set {} differs// from 3// 两个参数logger.debug("两个占位符,可以传两个参数{}----{}", 1, 2);// 多个参数(可变参数)logger.debug("debug:多个占位符,{},{},{},{}", 1, 2, 3, 4);// 多个参数(可变参数)logger.info("info:多个占位符,{},{},{},{}", 1, 2, 3, 4);// 多个参数(可变参数)logger.error("error:多个占位符,{},{},{},{}", 1, 2, 3, 4);}}

复制代码

结果:

2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[DEBUG] 普通的日志记录
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[DEBUG] 这是第0条记录
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[DEBUG] 这是第1条记录
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[DEBUG] 这是第2条记录
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[DEBUG] Set {} differs from 3
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[DEBUG] 两个占位符,可以传两个参数1----2
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[DEBUG] debug:多个占位符,1,2,3,4
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[INFO] info:多个占位符,1,2,3,4
2018-08-18 10:58:38 [cn.xm.exam.test.Slf4jTest]-[ERROR] error:多个占位符,1,2,3,4

 

注意:debug,info,error,等各个级别的方法都可以传入多个可变参数:

 

至于详细的日志级别的介绍参考我的另一篇博客:http://www.cnblogs.com/qlqwjy/p/7192947.html

 

2.关于正式开发中的日记记录的方法:(重要)

  项目中日志记录还是有很多说法的,比如保存日志级别,日志应该打印的信息,日志参数的设置等:

 

1.一般是将捕捉到的Exception对象作为日志记录的最后一个参数(会显示具体的出错信息以及出错位置),而且要放在{}可以格式化的参数之外,防止被{}转为e.toString()

例如一个标准的日记记录的方法:

复制代码

package cn.xm.exam.test;import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Slf4jTest {private static Logger log = LoggerFactory.getLogger(Slf4jTest.class);public static void main(String[] args) {openFile("xxxxxx");}public static void openFile(String filePath) {File file = new File(filePath);try {InputStream in = new FileInputStream(file);} catch (FileNotFoundException e) {log.error("can found file [{}]", filePath, e);}}}

复制代码

结果:

复制代码

2018-08-18 10:50:03 [cn.xm.exam.test.Slf4jTest]-[ERROR] can found file [xxxxxx]
java.io.FileNotFoundException: xxxxxx (系统找不到指定的文件。)at java.io.FileInputStream.open(Native Method)at java.io.FileInputStream.<init>(FileInputStream.java:146)at cn.xm.exam.test.Slf4jTest.openFile(Slf4jTest.java:22)at cn.xm.exam.test.Slf4jTest.main(Slf4jTest.java:16)

复制代码

 

注意: {}的作用是调用对应参数的toString()方法格式化日志,如果exception放在对应参数的位置上也会被格式化。所以,e要放在{}参数之外,而且只能放在最后一个。如果放在中间也不会被打印错误信息:

例如:

  • 只要放到{}之外的最后一个参数可以打印错误信息(包括信息和位置)

复制代码

    public static void openFile(String filePath) {File file = new File(filePath);try {InputStream in = new FileInputStream(file);} catch (FileNotFoundException e) {log.error("error:can found file [{}]", filePath, 3, e);}}

复制代码

结果:

复制代码

2018-08-18 11:11:18 [cn.xm.exam.test.Slf4jTest]-[ERROR] error:can found file [xxxxxx]
java.io.FileNotFoundException: xxxxxx (系统找不到指定的文件。)at java.io.FileInputStream.open(Native Method)at java.io.FileInputStream.<init>(FileInputStream.java:146)at cn.xm.exam.test.Slf4jTest.openFile(Slf4jTest.java:22)at cn.xm.exam.test.Slf4jTest.main(Slf4jTest.java:16)

复制代码

 

  • 放到{}之外的非最后一个参数不会打印错误信息

复制代码

    public static void openFile(String filePath) {File file = new File(filePath);try {InputStream in = new FileInputStream(file);} catch (FileNotFoundException e) {log.error("error:can found file [{}]", filePath,e,3);}}

复制代码

结果:

2018-08-18 11:10:38 [cn.xm.exam.test.Slf4jTest]-[ERROR] error:can found file [xxxxxx]

 

 

 

2.尽量不使用e.getMessage(),因为有的异常不一定有message,可以使用e.toString只会显示信息,不会显示出错的位置信息(不建议这种)

例如:

复制代码

package cn.xm.exam.test;import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Slf4jTest {private static Logger log = LoggerFactory.getLogger(Slf4jTest.class);public static void main(String[] args) {openFile("xxxxxx");}public static void openFile(String filePath) {File file = new File(filePath);try {InputStream in = new FileInputStream(file);} catch (FileNotFoundException e) {//下面两句的效果一样log.error("can found file [{}],cause:{}", filePath, e.toString());log.error("can found file [{}],cause:{}", filePath, e);}}}

复制代码

结果:

2018-08-18 10:53:31 [cn.xm.exam.test.Slf4jTest]-[ERROR] can found file [xxxxxx],cause:java.io.FileNotFoundException: xxxxxx (系统找不到指定的文件。)
2018-08-18 10:53:31 [cn.xm.exam.test.Slf4jTest]-[ERROR] can found file [xxxxxx],cause:java.io.FileNotFoundException: xxxxxx (系统找不到指定的文件。)

 

 

补充:一份上线系统使用的log4j.properties配置:

复制代码

log4j.rootLogger=info,Blog4j.appender.A=org.apache.log4j.ConsoleAppender
log4j.appender.A.layout=org.apache.log4j.PatternLayout
log4j.appender.A.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%nlog4j.appender.B=org.apache.log4j.RollingFileAppender
log4j.appender.B.File=E:\\test.log
log4j.appender.B.MaxFileSize=10MB
log4j.appender.B.MaxBackupIndex=5
log4j.appender.B.layout=org.apache.log4j.PatternLayout
log4j.appender.B.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n

复制代码

%d{yyyy-MM-dd HH:mm:ss} 也可以改为%d{yyyy-MM-dd HH:mm:ss,SSS} SSS代表至少输出3位数字的毫秒。如果4个S的话不足位补0。

 

解释:org.apache.log4j.RollingFileAppender是log4j的一个类,下面的配置都是它的属性的配置:(其中还有好多属性来自其父类,需要的时候我们可以去查阅)

 

log4j.appender.B.File    指定输出的日志的文件名称以及路径

log4j.appender.B.MaxFileSize   指定每个文件的最大大小

log4j.appender.B.MaxBackupIndex  指定文件达到文件大小之后最多重命名的文件数量

log4j.appender.B.layout  指定采用的样式

log4j.appender.B.layout.ConversionPattern  指定样式的格式(值一般固定)

 

测试代码:

复制代码

package cn.qlq.slf4jTest;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class Slf4jTest {private static Logger logger = LoggerFactory.getLogger(Slf4jTest.class);// slf4j日志记录器public static void main(String[] args) {while (true) {// 普通的日志记录logger.error("普通的日志记录");// {}占位符记录日志for (int i = 0; i < 3; i++) {logger.info("这是第{}条记录", i);}// 用\转义{}logger.info("Set \\{} differs from {}", "3"); // output:Set {} differs from 3// 两个参数logger.info("两个占位符,可以传两个参数{}----{}", 1, 2);}}}

复制代码

结果:(会产生对应的文件,test.log永远保存最新的日志,达到10M后会重命名文件为test.log.1,并将原来test.log.1命名为test.log.2....)

log内容如下:

 

 

补充:Threshold还可以指定输出的日志级别: (如果设置不起作用查看项目是不是用的log4j的包,有可能有多个log4j包,造成冲突)

  有时候我们需要把一些报错ERROR日志单独存到指定文件 ,这时候,Threshold属性就派上用场了,比如:

复制代码

log4j.rootLogger=info,B,Clog4j.appender.B=org.apache.log4j.RollingFileAppender
log4j.appender.B.Threshold=info
log4j.appender.B.File=E:\\info.log
log4j.appender.B.MaxFileSize=10MB
log4j.appender.B.MaxBackupIndex=5
log4j.appender.B.layout=org.apache.log4j.PatternLayout
log4j.appender.B.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%nlog4j.appender.C=org.apache.log4j.RollingFileAppender
log4j.appender.C.Threshold=error
log4j.appender.C.File=E:\\error.log
log4j.appender.C.MaxFileSize=10MB
log4j.appender.C.MaxBackupIndex=5
log4j.appender.C.layout=org.apache.log4j.PatternLayout
log4j.appender.C.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n

复制代码

 

上面B格式的就是info级别以上的日志,包括error信息 

  C格式的就是error级别以上的信息。

 

  当然这里有个提前  rootLogger里配置的level必须小于Threshold等级,否则无效 还是按照总的rootLogger里的level来输出,一般我们实际实用的话 rootLogger里配置DEBUG,然后某个文件专门存储ERRO日志,就配置下Threshold为ERROR

 

代码还是上面的测试代码:

结果:

 

 

 error.log内容:全是error级别以上的日志

 

info.log内容(info级别以上,包括error)

 

补充:log4j也可以对不同的包进行不同的配置,也就是针对不同的包采用不同的日志级别与日志控制器

  有时候我们也希望对不同的包采用不通的日志记录级别以及不同的日志记录方式。log4j完全可以做到这点,例如下面

默认包采用rootLogger的配置,info级别、在控制台与文件中进行显示;同时又修改了cn下面的a、b、c包的日志级别:

  cn.a只降低了级别为debug,输出方式还是console与file两种。(一般我们采用这种方式修改级别即可,如果再设置输出方式会在原来的基础上增加方式)

  cn.b级别设为info,方式设为console,实际是加了第三种方式

  cn.c级别设置为error,方式设为file,实际也是在rootLogger基础上增加第三种方式

   级别会以log4j.logger.XX级别为准,不管rootLogger级别高于对具体包的设置还是低于具体包的设置;输出方式会在rootLogger的基础上增加新的方式,如果没有额外的方式采用rootLogger的方式。

例如:(注意标红地方)

复制代码

log4j.rootLogger=info,console,filelog4j.logger.cn.a=debug
log4j.logger.cn.b=info,console
log4j.logger.cn.c=error,filelog4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%nlog4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=E:\\test.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n

复制代码

 

测试:

包结构:

 

Root

复制代码

package cn;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class RootClass {private static final Logger LOGGER = LoggerFactory.getLogger(RootClass.class);public static void main(String[] args) {LOGGER.debug("RootClass");LOGGER.info("RootClass");LOGGER.error("RootClass");}
}

复制代码

运行结果:

2018-10-23 19:38:04 [cn.RootClass]-[INFO] RootClass
2018-10-23 19:38:04 [cn.RootClass]-[ERROR] RootClass

 

 AC1

复制代码

package cn.a;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class AC1 {private static final Logger LOGGER = LoggerFactory.getLogger(AC1.class);public static void main(String[] args) {LOGGER.debug("AC1");LOGGER.info("AC1");LOGGER.error("AC1");}
}

复制代码

运行结果:

2018-10-23 19:38:31 [cn.a.AC1]-[DEBUG] AC1
2018-10-23 19:38:31 [cn.a.AC1]-[INFO] AC1
2018-10-23 19:38:31 [cn.a.AC1]-[ERROR] AC1

 

BC1:

复制代码

package cn.b;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class BC1 {private static final Logger LOGGER = LoggerFactory.getLogger(BC1.class);public static void main(String[] args) {LOGGER.debug("BC1");LOGGER.info("BC1");LOGGER.error("BC1");}
}

复制代码

运行结果:

2018-10-23 19:39:04 [cn.b.BC1]-[INFO] BC1
2018-10-23 19:39:04 [cn.b.BC1]-[INFO] BC1
2018-10-23 19:39:04 [cn.b.BC1]-[ERROR] BC1
2018-10-23 19:39:04 [cn.b.BC1]-[ERROR] BC1

 

CC1:

复制代码

package cn.c;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class CC1 {private static final Logger LOGGER = LoggerFactory.getLogger(CC1.class);public static void main(String[] args) {LOGGER.debug("CC1");LOGGER.info("CC1");LOGGER.error("CC1");}
}

复制代码

运行结果:

2018-10-23 19:39:28 [cn.c.CC1]-[ERROR] CC1

 

最后查看E:/test.log

复制代码

2018-10-23 19:38:04 [cn.RootClass]-[INFO] RootClass
2018-10-23 19:38:04 [cn.RootClass]-[ERROR] RootClass
2018-10-23 19:38:31 [cn.a.AC1]-[DEBUG] AC1
2018-10-23 19:38:31 [cn.a.AC1]-[INFO] AC1
2018-10-23 19:38:31 [cn.a.AC1]-[ERROR] AC1
2018-10-23 19:39:04 [cn.b.BC1]-[INFO] BC1
2018-10-23 19:39:04 [cn.b.BC1]-[ERROR] BC1
2018-10-23 19:39:28 [cn.c.CC1]-[ERROR] CC1
2018-10-23 19:39:28 [cn.c.CC1]-[ERROR] CC1

复制代码

 

补充:对父包设置日志级别,如果子包没有设置默认采用父包的设置,如果子包设置了会采用单独的设置

配置修改cn包设置以及对b包单独进行设置:

log4j.logger.cn=error
log4j.logger.cn.b=info,console

 

测试cn.d包默认采用cn包的error级别:

复制代码

package cn.d;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class DC1 {private static final Logger LOGGER = LoggerFactory.getLogger(DC1.class);public static void main(String[] args) {LOGGER.debug("D");LOGGER.info("D");LOGGER.error("D");}
}

复制代码

结果:

2018-12-29 13:30:09 [cn.d.DC1]-[ERROR] D

 

测试cn.b采用单独对cn.b包的配置

复制代码

package cn.b;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class BC1 {private static final Logger LOGGER = LoggerFactory.getLogger(BC1.class);public static void main(String[] args) {LOGGER.debug("BC1");LOGGER.info("BC1");LOGGER.error("BC1");}
}

复制代码

结果:

2018-12-29 13:31:27 [cn.b.BC1]-[INFO] BC1
2018-12-29 13:31:27 [cn.b.BC1]-[INFO] BC1
2018-12-29 13:31:27 [cn.b.BC1]-[ERROR] BC1
2018-12-29 13:31:27 [cn.b.BC1]-[ERROR] BC1

 

补充:上面的对包的具体设置日志级别虽然不受总的log4j.rootLogger的日志级别的限制,但是却受特殊的日志的Threshold属性的限制,也就是对具体包的设置必须高于其记录器的Threshold属性,否则以其记录器的Threshold属性为准

例如:修改上面的日志配置(每个日志记录器增加Threshold属性)

复制代码

log4j.rootLogger=info,console,filelog4j.logger.cn.a=debug
log4j.logger.cn.b=info,console
log4j.logger.cn.c=error,filelog4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Threshold=info
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%nlog4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.Threshold=error
log4j.appender.file.File=E:\\test.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n

复制代码

运行RootClass:

2018-11-12 23:02:44 [cn.RootClass]-[INFO] RootClass
2018-11-12 23:02:44 [cn.RootClass]-[ERROR] RootClass

 

运行AC1:

2018-11-12 23:03:08 [cn.a.AC1]-[INFO] AC1
2018-11-12 23:03:08 [cn.a.AC1]-[ERROR] AC1

 

运行BC1:

2018-11-12 23:03:33 [cn.b.BC1]-[INFO] BC1
2018-11-12 23:03:33 [cn.b.BC1]-[INFO] BC1
2018-11-12 23:03:33 [cn.b.BC1]-[ERROR] BC1
2018-11-12 23:03:33 [cn.b.BC1]-[ERROR] BC1

 

运行CC1:

2018-11-12 23:04:25 [cn.c.CC1]-[ERROR] CC1

 

查看test.log:

2018-11-12 23:02:44 [cn.RootClass]-[ERROR] RootClass
2018-11-12 23:03:08 [cn.a.AC1]-[ERROR] AC1
2018-11-12 23:03:33 [cn.b.BC1]-[ERROR] BC1
2018-11-12 23:04:25 [cn.c.CC1]-[ERROR] CC1
2018-11-12 23:04:25 [cn.c.CC1]-[ERROR] CC1

所以总结上面的日记记录级别的设置优先级可以总结为:Threshold  > 具体包的设置  >  rootLogger的全局配置

【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402653.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JdbcType类型和Java类型的对应关系

From: https://www.cnblogs.com/tongxuping/p/7134113.html 在Oracle中有些字段不是必填时在用户使用的时候会出现数据null的情况。这个时候在Oracle中是无法进行插入的。 1 JDBC Type Java Type 2 CHAR String 3 VARCHAR String 4 L…

MyBatis Generator配置文件翻译

From: https://www.cnblogs.com/GaiDynasty/p/4088531.html <classPathEntry> 驱动文件指定配置项 <classPathEntry location"/Program Files/IBM/SQLLIB/java/db2java.zip" /> <columnOverride> 将数据库中的字段重命名为实体类的属性 colu…

SpringBoot系列十:SpringBoot整合Redis

From: https://www.cnblogs.com/leeSmall/p/8728231.html 声明&#xff1a;本文来源于MLDN培训视频的课堂笔记&#xff0c;写在这里只是为了方便查阅。 1、概念&#xff1a;SpringBoot 整合 Redis 2、背景 Redis 的数据库的整合在 java 里面提供的官方工具包&#xff1a;j…

海贼王革命家—龙—实力到底如何?

龙——整个海贼王世界中最神秘的人物&#xff0c;令世界政府最担心的存在&#xff0c;是所有迷最为期待的实力展现&#xff0c;他的身上好像有着无数的秘密等着尾田为我们揭晓。 路飞的父亲——未来的海贼王、卡普的儿子——海军英雄、革民军首领——唯一可以跟世界政府抗衡的组…

MyBatis 实践

From&#xff1a; https://www.cnblogs.com/luyiba/p/6303717.html MyBatis简介 MyBatis前身是iBatis,是一个基于Java的数据持久层/对象关系映射(ORM)框架. MyBatis是对JDBC的封装,使开发人员只需关注SQL本身,而不需花费过多的精力去处理如注册驱动、设置参数、创建Connectio…

皮克斯背后新技术

2019独角兽企业重金招聘Python工程师标准>>> 皮克斯每创作一部动画作品总会给观众带来或多或少的惊喜&#xff0c;而很多影视动画从业人员也习惯于关注他们的每一部作品的制作&#xff0c;因为新技术往往就在这些制作过程中诞生&#xff0c;如今皮克斯动画已经成为C…

数据结构:二叉查找树(C语言实现)

数据结构&#xff1a;二叉查找树 二叉查找树 基础知识 关于二叉树的基础知识&#xff0c;请看我的一篇博客:二叉树的链式存储 二叉查找树的特征 二叉查找树或者是一棵空树&#xff0c;或者是具有下列性质的二叉树&#xff1a;  1.若其左子树不空&#xff0c;则左子树上所有结…

Effective Java 电子书 apk版本下载

下载安装包以后&#xff0c;安装即可阅读该书了&#xff0c;并且实时展示每章节代码哦&#xff0c;并且可以运行哦&#xff0c;赶快下载体验吧. Effective Java中文第二版下载地址&#xff1a;下载 应用截图&#xff1a; 转载于:https://www.cnblogs.com/spring87/p/6090010.ht…

给你的Mr.Right画张择偶地图像

爱一个人就算做不到爱他的全部&#xff0c;至少也应该尊重他的真实&#xff0c;而不是苛求他变成你想要的样子。 娶妻当娶郭芙蓉&#xff0c;经典语录。我是郭芙蓉&#xff0c;我不会武功&#xff0c;我来自江湖&#xff0c;我与众不同。再苦再累&#xff0c;就当自己是二百五&…

java实现HTTP请求的三种方式

From: https://www.cnblogs.com/hhhshct/p/8523697.html 目前JAVA实现HTTP请求的方法用的最多的有两种&#xff1a;一种是通过HTTPClient这种第三方的开源框架去实现。HTTPClient对HTTP的封装性比较不错&#xff0c;通过它基本上能够满足我们大部分的需求&#xff0c;HttpClien…

SpringBoot时间戳与MySql数据库记录相差14小时排错

From: http://www.cnblogs.com/jason1990/archive/2018/11/28/10032181.html 项目中遇到存储的时间戳与真实时间相差14小时的现象,以下为解决步骤. 问题 CREATE TABLE incident (id int(11) NOT NULL AUTO_INCREMENT,created_time timestamp NOT NULL DEFAULT CURRENT_TIMES…

mysql重置root密码方法

2019独角兽企业重金招聘Python工程师标准>>> 1. 先关闭mysqld 2. 运行&#xff1a; mysqld_safe --skip-grant-tables 3. 另开一个窗口&#xff0c;用 mysql -uroot 登录mysql&#xff0c;执行 UPDATE mysql.user SET PasswordPASSWORD(你的密码) WHERE User…

插入排序之C++实现

描述 插入排序是一种简单直观的排序算法。它的基本思想是将一个待排序的数据序列分为已排序和未排序两部分&#xff0c;每次从未排序序列中取出一个元素&#xff0c;然后将它插入到已排序序列的适当位置&#xff0c;直到所有元素都插入完毕&#xff0c;即完成排序。 实现思路…

Spring在Java Filter注入Bean为Null的问题解决

From: https://www.cnblogs.com/EasonJim/p/7666009.html 在Spring的自动注入中普通的POJO类都可以使用Autowired进行自动注入&#xff0c;但是除了两类&#xff1a;Filter和Servlet无法使用自动注入属性。&#xff08;因为这两个归Web容器管理&#xff09;可以用init&#xf…

Mybatis:resultMap的使用总结

From: https://www.cnblogs.com/kenhome/p/7764398.html Mybatis的介绍以及使用&#xff1a;http://www.mybatis.org/mybatis-3/zh/index.html resultMap是Mybatis最强大的元素&#xff0c;它可以将查询到的复杂数据&#xff08;比如查询到几个表中数据&#xff09;映射到一个…

MyBatis总结六:resultMap详解(包含多表查询)

From: https://www.cnblogs.com/Alex-zqzy/p/9296039.html 简介&#xff1a;   MyBatis的每一个查询映射的返回类型都是ResultMap&#xff0c;只是当我们提供的返回类型属性是resultType的时候&#xff0c;MyBatis对自动的给我们把对应的值赋给resultType所指定对象的属性&a…

基于beego一键创建RESTFul应用

2019独角兽企业重金招聘Python工程师标准>>> API应用开发入门 Go是非常适合用来开发API应用的&#xff0c;而且我认为也是Go相对于其他动态语言的最大优势应用。beego在开发API应用方面提供了非常强大和快速的工具&#xff0c;方便用户快速的建立API应用原型&#…

MyBatis中in的使用

From: https://www.cnblogs.com/w-bb/articles/6378031.html foreach的主要用在构建in条件中&#xff0c;它可以在SQL语句中进行迭代一个集合。 foreach元素的属性主要有 item&#xff0c;index&#xff0c;collection&#xff0c;open&#xff0c;separator&#xff0c;close…

MyBatis总结五:#{}和${}的用法和区别

From: https://www.cnblogs.com/blazeZzz/p/9295634.html #{}的用法&#xff1a; 我们发现&#xff0c;在Mapper.xml映射文件中&#xff0c;经常使用#{属性名} 来作为SQL语句的占位符&#xff0c;来映射Sql需要的实际参数 如果只有一个参数 <select id"getUserById&…

iOS多视图代码操作

2019独角兽企业重金招聘Python工程师标准>>> 摘抄 http://supershll.blog.163.com/blog/static/370704362012112021115/ 2.1.1 层次结构 基于树的层次结构对iPhone屏幕上的可见内容进行排序。从主窗口开始&#xff0c;视图以特殊的分层方式布置。所有视图都可以有…