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,一经查实,立即删除!

相关文章

PS不能存储,因为程序错误

当PS中遇到不能存储文件&#xff0c;因为程序错误时&#xff0c;可以这样&#xff1a; http://www.zcool.com.cn/article/ZMTgwOTQw.html 转载于:https://www.cnblogs.com/kjcy8/p/6072599.html

MEF相关总结

&#xff08;1&#xff09;蛮好的一篇文章&#xff1a;LoveJenny的MEF 打造的插件系统 一个简单单很能容易理解的例子讲解了MEF. &#xff08;2&#xff09;原理方面的看Bēniaǒ成长笔记的《MEF程序设计指南》博文汇总 &#xff08;3&#xff09;http://www.cnblogs.com/pszw/…

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…

java实现计算字符串表达式

ScriptEngineManager manager new ScriptEngineManager(); ScriptEngine engine manager.getEngineByName("JavaScript");Object result engine.eval("12");转载于:https://www.cnblogs.com/highfly2012/p/6080374.html

Android:Application

1.Application是程序真正的入口&#xff0c;启动时会先执行Application再执行其他组件。2.建立自己的Application类&#xff0c;需要在xml里将application修改自己的application类&#xff1a;<applicationandroid:name"com.example.aexh_19_application.MyApplication…

实体entity、JavaBean、Model、POJO、domain的区别

From: https://blog.csdn.net/u011665991/article/details/81201499 Java Bean、POJO、 Entity、 VO &#xff0c; 其实都是java 对象&#xff0c;只不过用于不同场合罢了。 按照 Spring MVC 分层结构&#xff1a; JavaBean: 表示层 &#xff08;Presentation Layer&#xf…

【Linux_Fedora_系统管理系列】_1_用户登录和系统初始配置

发现一个问题&#xff0c;在FC14 的Firefox浏览器中&#xff0c;编辑和排版好的博文&#xff0c;在windows下用chrome或者猎豹浏览器打开后&#xff0c;排版就变得阅读 不是很容易里&#xff0c;而且经常不经意的断行。不知道园子的管理人员时候注意到了这个问题。 Linux系统的…

HDU 2202 计算几何

最大三角形 Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4015 Accepted Submission(s): 1433 Problem Description老师在计算几何这门课上给Eddy布置了一道题目&#xff0c;题目是这样的&#xff1a;给定二维…

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;他的身上好像有着无数的秘密等着尾田为我们揭晓。 路飞的父亲——未来的海贼王、卡普的儿子——海军英雄、革民军首领——唯一可以跟世界政府抗衡的组…

python模块介绍- xlwt 创建xls文件(excel)

python模块介绍- xlwt 创建xls文件&#xff08;excel&#xff09; 2013-06-24磁针石 #承接软件自动化实施与培训等gtalk&#xff1a;ouyangchongwu#gmail.comqq 37391319 博客:http://blog.csdn.net/oychw #版权所有&#xff0c;转载刊登请来函联系 # 深圳测试自动化python项目…

SpringBoot(六):SpringBoot整合Redis

From: https://blog.csdn.net/plei_yue/article/details/79362372 前言 在本篇文章中将SpringBoot整合Redis&#xff0c;使用的是RedisTemplate&#xff0c;分别实现了SpringBoot与redis的单机版、集群版、哨兵模式的整合。 Maven依赖 <!-- 整合redis --> <…

[4]Telerik Grid 简单使用方法

1.columns <% Html.Telerik().Grid(Model).Name("Orders").Columns(columns >{//绑定列名columns.Bound(o > o.OrderID);//隐藏字段columns.Bound(o > o.OrderID).Hidden(true); //绑定列标题 columns.Bound(o > o.OrderDate).Title("…

Springboot 2.x版本 RedisCacheManager 类的配置,【与1.x 略有不同】

From: https://blog.csdn.net/qq_15071263/article/details/82897330 文章目录 Springboot 2.x版本 RedisCacheManager 类的配置&#xff0c;【与1.x 略有不同】 1、1.x 配置方式 2、2.x 配置方式 Springboot 2.x版本 RedisCacheMan…

.net应用程序中添加chm帮助文档打开显示此程序无法显示网页问题

在做.net大作业时添加了chm帮助文档结果在打开时显示“此程序无法显示网页问题”&#xff0c;但是把帮助文档拷到别的路径下却显示正常&#xff0c; 经过从网上查找&#xff0c;终于找到了答案&#xff1a; (1)、chm文件的路径中不能含有“#”“%”等字符&#xff0c;当含有这些…

新磁盘创建lvm并挂载

1 ### 1.查看硬盘2 fdisk -l3 4 ### 删除分区5 fdisk /dev/sdc6 ### 按d删除&#xff0c;按w保存并退出7 8 ### 创建pv9 pvcreate /dev/sdc 10 11 ### 创建 vg 12 vgcreate vg_hdp /dev/sdc 13 14 ### 创建 lv 15 lvcreate -L 200G -n lv_hdp vg_hdp 16 17 ### 格式化 lv 18…

用unison来同步你的远程文件夹 - Fwolf's Blog

用unison来同步你的远程文件夹 - Fwolfs Blog: "" (Via.)转载于:https://www.cnblogs.com/devops/p/3157101.html

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…