1 简述
1.1 简述
commons-logging和slf4j(slf4j-api.jar)都是日志类库的接口,供客户端使用,而没有提供实现!
log4j,logback等等才是日志的真正实现。
- 日志(类库)
- 作用:可以调试程序,就像输出一样
- Java语言的日志类库:
- Log4J [Log For Java]
- log4j 是Apache为java提供日志管理的工具
- 真正实现日志功能的日志类库
- Logback
- java.util.logging
- SLF4J [Simple Logging Facade(外观/表面) For Java]
- 1个 日志标准/适配器
- 通过调用slf4j的API统一打印日志,而可忽略其他日志的具体方法
- 1个 日志标准/适配器
- Log4J [Log For Java]
- slf4j只是1个日志标准,并不是日志系统的具体实现。如果项目只有slf4j的包,是没有办法实现日志功能的。
- 而如果开发者已使用log4j,那么:开发者已经对于在if条件中使用debug语句这种变通方案十分熟悉了,但SLF4J的占位符就比这个好用得多。
1.2 安装/依赖[slf4j+log4j]
方式1:手动导入JAR包(slf4j-api-1.7.5.jar、slf4j-log4j-1.7.5.jar、log4j-1.2.16.jar)或配置如下Maven依赖:
- slf4j-api:提供接口
- slf4j-log4j[xx]:提供Log具体的实现
<!-- Log4j 依赖包 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.9.1</version>
</dependency>
<!-- slf4j 依赖包 -->
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.2</version>
</dependency>
1.3 配置文件
log4j.properties
log4j.xml
log4j2.xml(log4j/org.apache.logging.log4j:log4j在2.0.0及以后,被拆分为 log4j-api与log4j-core,对应的配置文件为 log4j2.xml)
2 LOG4J
2.0 Log4J的配置
如果没有调用BasicConfigurator.configure(),PropertyConfigurator.configure()或DOMConfigurator.configure()方法,Log4j会自动加载CLASSPATH下名为log4j.properties的配置文件。如果把此配置文件改为其他名字,例如my.properties,程序虽然仍能运行,但会报出不能正确初始化Log4j系统的提示。这时可以在程序中加上:PropertyConfigurator.configure("classes/my.properties");
配置Log4j环境就是指配置root Logger,包括把Logger为哪个级别,为它增加哪些Appender,以及为这些Appender设置Layout,等等。因为所有其他的Logger都是root Logger的后代,所以它们都继承了root Logger的性质。这些可以通过设置系统属性的方法来隐式地完成,也可以在程序中调用XXXConfigurator.configure()方法来显式地完成。有以下几种方式来配置Log4j。
- A:配置放在文件里,通过环境变量传递文件名等信息,利用Log4j默认的初始化过程解析并配置。
- B:配置放在文件里,通过应用服务器配置传递文件甸等信息,利用一个特定的Servlet来完成配置。
- C:配置放在文件里,通过命令行org.apache.log4j.PropertyConfigurator.configure(args[])解析log4j.properties文件并配置Log4j
- D:配置放在文件里,在程序中直接调用org.apache.log4j.BasicConfigor.configure()方法,或在程序中完成相关配置(↓)
- step1:用默认的方式创建PatternLayout对象p:
- PatternLayout p = new PatternLayout("%-4r[%t]%-5p%c%x-%m%n");
- step2: 用p创建ConsoleAppender对象a,目标是System.out,标准输出设备:
- ConsoleAppender a = new CpnsoleAppender(p,ConsoleAppender.SYSTEM_OUT);
- step3: 为root Logger增加一个ConsoleAppender p:
- rootLogger.addAppender(p);
- step4: 把rootLogger的log level设置为DUBUG级别
- rootLogger.setLevel(Level.DEBUG);
- step1:用默认的方式创建PatternLayout对象p:
- E:org.apache.log4j.DOMConfigurator.configure(String filename);
2.1 核心概念/3大组件
Log4j有3大组件:
- 【日志器(Logger)】:用来输出消息的类,可以输出不同级别的,比如错误消息,警告消息等
- 创建日志器
- Logger log = Logger.getLogger(calssNameX.class); log.info(); log.debug(); [log4J方式]
- Logger logger = LoggerFactory.getLogger(calssNameX.class); log.info(); log.debug();[slf方式(推荐)]
- 根日志器(rootLogger)
- 若需要输出到多个位置的时候可用逗号隔开: log4j.rootLogger=info, A, B
- 创建日志器
在配置文件里,需要为log4j.properties配置一个根日志器:
log4j.rootLogger=DEBUG,AA
log4j.rootLogger=WARN
log4j.APPENDER.AA=org.apache.log4j.ConsoleAppender
- 【输出源(Appender)】:日志输出的目标。日志输出到哪里去(文件、控制台)
- org.apache.log4j.ConsoleAppender: 向控制台输出日志
- org.apache.log4j.FileAppender: 向文件输出日志
- org.apache.log4j.DailyRollingFileAppender
- org.apache.log4j.RollingFileAppender
log4j.appender.AA.File=../log.txt
log4j.appender.AA.LAYOUT=org.apache.log4j.SimpleLayout
log4j.appender.AA.DatePatten='yyyy-MM-dd'
- 【格式化器(Layout)】:对输出消息进行格式化,比如添加日期
- org.apache.log4j.PatternLayout
- org.apache.log4j.SimpleLayout
- org.apache.log4j.HTMLLayout
- org.apache.log4j.TTCCLayout
2.2 日志级别(从高到低)
- OFF:最高等级,用于关闭所有日志记录
- FATAL:重大错误级(系统有问题)
- ERROR:错误级(一个模块有问题)
- WARN:警告级
- INFO:信息级。可以查看程序执行的流程
- DEBUG:调试。用来调试程序的bug及显示
- ALL:最低等级。用于打开所有日志记录
debug,info,error等日志级别的方法都可传入多个可变参数:(以debug为例)
2.3.1 日志器[Logger]
- 特点
- Logger(log4j)记录的是当前类的日志,不是每个实例的日志
- 使用static修饰的属性
- 只要有一个记录就可以了
- private static final Logger logger = LoggerFactory.getLogger(Slf4jTest.class.getName());// slf4j日志记录器
- 每个Logger都配有1个日志级别(Log Level):用来控制日志信息的输出。
- Logger(log4j)记录的是当前类的日志,不是每个实例的日志
- rootLogger(根日志器)
- root Logger(根Logger)是所有Logger的祖先
- 它总是存在的
- 它不可以通过名字获得
- public static Logger Logger.getRootLogger();
- public static Logger Logger.getLogger(Class clazz)
2.3.2 输出源[Appender]
org.apache.log4j.ConsoleAppender
Threshold=WARN:指定日志信息的最低输出级别,默认DEBUG
ImmediateFlush=true:表示所有消息都会被立即输出,设为false则不输出,默认值是true
Target=System.err:默认值是System.out
org.apache.log4j.FileAppender
Threshold=WARN:指定日志信息的最低输出级别,默认DEBUG
ImmediateFlush=true:表示所有消息都会被立即输出,设为false则不输出,默认true
Append=false:true表示消息增加到指定文件中,false则将消息覆盖指定的文件内容,默认true
File=D:/logs/logging.log4j:指定消息输出到logging.log4j文件
org.apache.log4j.DailyRollingFileAppender(常用)
Threshold=WARN:指定日志信息的最低输出级别,默认DEBUG
ImmediateFlush=true:表示所有消息都会被立即输出,设为false则不输出,默认true
Append=false:true表示消息增加到指定文件中,false则将消息覆盖指定的文件内容,默认true
File=D:/logs/logging.log4j:指定当前消息输出到logging.log4j文件
DatePattern='.'yyyy-MM:每月滚动一次日志文件,即每月产生一个新的日志文件。
当前月的日志文件名为logging.log4j,前一个月的日志文件名为logging.log4j.yyyy-MM
另外,也可以指定按周、天、时、分等来滚动日志文件,对应的格式如下:
1)'.'yyyy-MM:每月
2)'.'yyyy-ww:每周
3)'.'yyyy-MM-dd:每天
4)'.'yyyy-MM-dd-a:每天两次
5)'.'yyyy-MM-dd-HH:每小时
6)'.'yyyy-MM-dd-HH-mm:每分钟
org.apache.log4j.RollingFileAppender
Threshold=WARN:指定日志信息的最低输出级别,默认DEBUG
ImmediateFlush=true:表示所有消息都会被立即输出,设为false则不输出,默认true
Append=false:true表示消息增加到指定文件中,false则将消息覆盖指定的文件内容,默认true
File=D:/logs/logging.log4j:指定消息输出到logging.log4j文件
MaxFileSize=100KB:后缀可以是KB,MB或者GB。在日志文件到达该大小时,将会自动滚动,即将原来的内容移到logging.log4j.1文件
MaxBackupIndex=2:指定可以产生的滚动文件的最大数,例如,设为2则可以产生logging.log4j.1,logging.log4j.2两个滚动文件和一个logging.log4j文件
2.3.3 日志格式化器[Layout]
指定logger输出内容及格式: log4j.appender.appenderName.layout=className
- org.apache.log4j.PatternLayout(最常用,可以灵活地指定布局模式)根据指定的转换模式格式化日志输出,或若未指定任何转换模式,就用默认的转化模式格式
- ConversionPattern=%m%n:设定以怎样的格式显示消息
- org.apache.log4j.HTMLLayout:(以HTML表格形式布局)格式化日志输出为HTML表格形式
- LocationInfo=true:输出java文件名称和行号,默认false
- Title=My Logging: 默认值是Log4J Log Messages
- org.apache.log4j.SimpleLayout:(包含日志信息的级别和信息字符串)以一种非常简单的方式格式化日志输出,它打印三项内容:级别-信息
- org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等信息)
【patterm参数】
%p: 日志级别<从高到低>(OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL)
%c: 输出日志信息所属的类目,通常就是所在类的全名。可写为%c{num},表示取完整类名的层数,从后向前取,比如%c{2}取 "cn.qlq.exam"类为"qlq.exam"。
%r: 输出自应用启动到输出该日志耗费的毫秒数
%d: 输出日志时间点的日期或时间。默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy-MM-dd HH:mm:ss,SSS},SSS为毫秒数(也可以写为SS,只不过SSS如果不足三位会补0),输出类似:2011-10-18 22:10:28,021
%m: 输出代码中指定的消息,产生的日志具体信息
%n: 输出一个回车换行符,Windows平台为"
",Unix平台为"
"输出日志信息换行
%t: 输出当前产生日志的线程名称
%F: 输出日志消息产生时所在的文件名称
%x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中
%%: 输出1个"%"字符
%l: 输出日志事件的位置,相当于%c.%M(%F:L)的组合,包括类全名、方法、文件名以及在代码中行数。例如:cn.xm.test.PlainTest.main(PlanTest.java:12)
3 Slf4J
3.1 特点
- 不同于其他日志类库,与其它日志类库有很大的不同
- 不是1个真正的日志实现,而是1个抽象层
- 它允许你在后台使用任意一个日志类库
- 使代码独立于任一特定的日志API;
- 无需忍受加载和维护新的日志框架的痛苦
- 占位符(place holder):"{}"
- 占位符非常类似于在String的format()方法中的%s
- 因为它会在运行时被某个提供的实际字符串所替换
- 不仅降低了代码中字符串连接次数,而且还节省了新建的String对象
- 可以在运行时延迟字符串的建立。
- 这意味着只有需要的String对象才被建立
- 占位符非常类似于在String的format()方法中的%s
4 问题集
- 问题:log4j:WARN No appenders could be found for logger...log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
1: log4j.properties 放置在/src/log4j.properties
2: org.apache.log4j.BasicConfigurator.configure(); //自动快速地使用缺省Log4j环境
5 实际项目开发中的日志应用
1 一般是将捕捉到的Exception对象(e)作为日志记录的最后一个参数(会显示具体的出错信息以及出错位置),而且要放在{}可以格式化的参数之外。防止被{}转为e.toString()
2 可使用e.toString(),而尽量不使用e.getMessage()。因为有的异常不一定有message,可以使用e.toString只会显示信息,不会显示出错的位置信息(不建议这种)
//link - blog: https://www.cnblogs.com/qlqwjy/p/9275415.html
package cn.johnnyzen.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) {
//Exception对象(e)作为日志记录的最后一个参数(会显示具体的出错信息以及出错位置),而且要放在{}可以格式化的参数之外
log.error("can found file [{}]", filePath, e);
}
}
}
2020-06-04 00:30:03 [cn.johnnyzen.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)