介绍:slf4j-api提供接口,slf4j-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;
protected
Level(int level, String levelStr, int syslogEquivalent) {
super(level, levelStr, syslogEquivalent);
}
public
static
Level 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>
或者手动导入下面的包:
image1 基本介绍
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),在代码中表示为“{}”的特性。占位符是一个非常类似于在String的format()方法中的%s,因为它会在运行时被某个提供的实际字符串所替换。这不仅降低了你代码中字符串连接次数,而且还节省了新建的String对象。通过使用SLF4J,你可以在运行时延迟字符串的建立,这意味着只有需要的String对象才被建立。而如果你已经使用log4j,那么你已经对于在if条件中使用debug语句这种变通方案十分熟悉了,但SLF4J的占位符就比这个好用得多。
3.slf4j的简单用法
1.简单使用
1.配置文件(log4j.properties)
log4j.rootLogger=debug, C
log4j.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%n
log4j.appender.B=org.apache.log4j.FileAppender
log4j.appender.B.File=E:\\log.log
log4j.appender.B.layout=org.apache.log4j.SimpleLayout
log4j.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=gbk
log4j.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},SSS为毫秒数(也可以写为SS,只不过SSS如果不足三位会补0),输出类似:2011-10-18 22:10:28,021%r: 输出自应用启动到输出该日志耗费的毫秒数%t: 输出产生日志的线程名称%l: 输出日志事件的位置,相当于%c.%M(%F:L)的组合,包括类全名、方法、文件名以及在代码中行数。例如:cn.xm.test.PlainTest.main(PlanTest.java:12)%c: 输出日志信息所属的类目,通常就是所在类的全名。可写为%c{num},表示取完整类名的层数,从后向前取,比如%c{2}取 "cn.qlq.exam"类为"qlq.exam"。%M: 输出产生日志信息的方法名%x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中
%%: 输出一个"%"字符
%F: 输出日志消息产生时所在的文件名称
%L: 输出代码中的行号
%m: 输出代码中指定的消息,产生的日志具体信息
%n: 输出一个回车换行符,Windows平台为"\r\n",Unix平台为"\n"输出日志信息换行
测试代码如下
package cn.xm.exam.test;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PlainTest {
private static final Logger LOGGER = LoggerFactory.getLogger(PlainTest.class);
public static void main(String[] args) throws IOException, InterruptedException {
LOGGER.debug("线程开始");
Thread.sleep(1 * 1000);
test1();
}
public static void test1() {
LOGGER.debug("test1方法");
}
}
(1)配置1
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%p] %m%n
结果:
2019-08-21 22:13:50 [DEBUG] 线程开始
2019-08-21 22:13:51 [DEBUG] test1方法
(2) %t 线程名
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%p] [%t] %m%n
结果(线程名为main):
2019-08-21 22:16:09 [DEBUG] [main] 线程开始
2019-08-21 22:16:10 [DEBUG] [main] test1方法
(3) %l
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%l] %m%n
结果:
2019-08-21 22:19:53 [cn.xm.exam.test.PlainTest.main(PlainTest.java:12)] 线程开始
2019-08-21 22:19:54 [cn.xm.exam.test.PlainTest.test1(PlainTest.java:19)] test1方法
(4)%r 产生日志距离应用启动的毫秒数
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%l] [%r] %m%n
结果:
2019-08-21 22:34:27 [cn.xm.exam.test.PlainTest.main(PlainTest.java:12)] [0] 线程开始
2019-08-21 22:34:28 [cn.xm.exam.test.PlainTest.test1(PlainTest.java:19)] [1002] test1方法
测试代码
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,等各个级别的方法都可以传入多个可变参数:
image
网友评论