java日志
在程序开发中,日志是一个很重要的角色,其主要用于记录程序运行的情况,以便于程序在部署之后的排错调试等。
日志的概念
(1)日志的作用及好处
-
查看程序的运行状态以及程序的运行轨迹,方便对程序进行分析。
-
系统问题排查,日志结合异常处理,可快速发现程序发生异常的地点。
-
查看系统运行时间,为系统性能优化提供相关的依据。
-
安全审计,通过系统日志分析,可以判断一些非法攻击,非法调用,以及系统处理过程中的安全隐患。
(2)最简单的日志
最简单的日志使用便是System.out.print()
public class Application(){
public static void main(String[] args){
// 设置日期格式
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// new Date()为获取当前系统时间
String beginTime = df.format(new Date());
System.out.print(beginTime + "程序开始了......");
...
...
...
String endTime = df.format(new Date());
System.out.print(endTime + "程序结束了......");
}
}
优点:直观、方便
缺点:
-
System.out.print
和java
运行程序运行在同一线程,业务程序会等待System.out.print
的动作,导致资源被占用。 -
System.out.print
是在控制台输出,只能输出到控制台,没有存储到日志文件当中。 -
不规范。
(3)日志框架
Java 拥有功能和性能都非常强大的日志库,但不幸的是,日志库并不止一个,如JUL(Java Util Log)
、JCL(Commons Logging)
、Log4j
、SLF4J
、Logback
、Log4j2
等等的日志工具,那么到底使用那一个呢?
Java日志的发展史
日志最早出现的是Apache开源社区的Log4j
,此日志确实是应用最广泛的日志工具,成为了 Java 日志事实上的标准。之后, Java 的开发主体 Sun 公司为了抢回市场,在Jdk1.4
中增加了与Log4j
没有关联的 JUL(java.util.logging
)日志实现,用于对抗 Log4j
,但是却成为了 Java 目前记录日志局面混乱的开端。
当项目使用的第三方库使用了不同的日志工具时,就会造成一个应用中存在着多种日志的情况,由于各种日志之间互相没有关联,替换和统一日志工具也就变成了一件比较棘手的事情。为解决多种log工具存在的问题,Apache 开源社区提供了一个日志框架作为日志的抽象,叫 commons-logging
,也被称为 JCL
。JCL
会对各种日志接口进行抽象,抽象出一个接口层,实现对每个日志框架都进行适配,在使用日志工具时直接使用抽象接口即可,完成了各种主流日志框架的兼容实现(Log4j
、JUL
、simplelog
等),较好的解决了上述问题。
JCL
出现之后,元老级日志Log4j
的作者 觉得 JCL
不够优秀,所以他再度开发了一套更优雅的日志框架 SLF4J
(Simple Logging Facade for Java),即简单日志门面,并为 SLF4J
实现了一个亲儿子——logback
日志框架。在弄完后,觉得还是得照顾一下自己的“大儿子”log4j
,于是对log4j
进行升级,便出现了Log4j2
。
java common logging
和 SLF4J
都是日志的接口,供用户使用,而没有提供实现,Log4j
、JUL
、logback
、Log4j2
等等才是日志的真正实现。他们之间的关系可看作电脑与打印机设备之间的关系,电脑连接多台没有关联的打印机,用户只需用电脑选择打印机进行打印。不相同的是,当我们调用日志接口时,接口会自动寻找恰当的实现,返回一个合适的实例给我们服务。这些过程都是透明化的,用户不需要进行任何操作。(如不理解,请继续往后阅读)
日志级别
日志级别 | 日志描述 |
---|---|
OFF | 关闭:最高级别,不输出日志 |
FATAL | 致命:输出非常严重的可能会导致应用程序终止的错误。 |
ERROR | 错误:严重错误,主要是程序出错。 |
WARN | 警告:输出可能潜在的危险状况。 |
INFO | 信息:输出应用运行过程的详细信息。 |
DEBUG | 调试:输出更细致的对调试应用有用的信息。 |
TRACE | 跟踪:输出更细致的程序运行轨迹。 |
ALL | 所有:输出所有级别信息。 |
最常见的日志级别为Debug、Info、Warn、Error
日志优先级别从低到高顺序为:ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF
Log4j日志框架
最先出现的日志框架,是 Apache 的一个开源项目。使用Log4j可以通过配置文件来灵活进行配置,控制日志信息的输出格式和目标,而不需要修改程序代码。
使用日志级别
日志级别 | 日志描述 |
---|---|
OFF | 关闭:最高级别,不输出日志 |
FATAL | 致命:输出非常严重的可能会导致应用程序终止的错误。 |
ERROR | 错误:严重错误,主要是程序出错。 |
WARN | 警告:输出可能潜在的危险状况。 |
INFO | 信息:输出应用运行过程的详细信息。 |
DEBUG | 调试:输出更细致的对调试应用有用的信息。 |
TRACE | 跟踪:输出更细致的程序运行轨迹。 |
ALL | 所有:输出所有级别信息。 |
日志优先级别从高到低顺序为: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL
apache建议使用4级,即 ERROR、WARN、INFO、DEBUG
使用步骤
(1)引入相关依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
(2)配置log4j
核心配置文件log4j.properties
先认识一下Log4j
三大组件Logger、Appender、Layout。
-
Logger: 日志记录器,日志记录的核心类,用于输出不同日志级别的消息。
-
Appender: 日志输出目标,用于指定日志输出的目的地,如控制台、文件等等。
-
Layout: 日志格式化器,用于指定日志按照什么格式输出,是日志输出的格式化器。
配置样例:
# Global logging configuration
######################### Logger ############################
# 设置日志输出级别以及输出目的地,等号后第一个参数写日志级别,级别后面写输出目的地,目的地名可自定义,多个目的地以逗号分开。
# 设置根Logger,默认输出DEBUG以上的记录,输出目标(appender)为CONSOLE、LOGFILE
log4j.rootLogger=DEBUG,CONSOLE,LOGFILE
######################### Appender #########################
# 对目标CONSOLE进行配置(与Logger对应)
# 设置日志目标类型为org.apache.log4j.ConsoleAppender控制台类型
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
# 输出到的目的地
log4j.appender.CONSOLE.Target = System.out
# 指定控制台输出日志级别
log4j.appender.CONSOLE.Threshold = DEBUG
# 默认值是 true, 表示是否立即输出
log4j.appender.CONSOLE.ImmediateFlush = true
# 设置编码方式
log4j.appender.CONSOLE.Encoding = UTF-8
# 设置日志输出布局方式Layout
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
# 如果日志输出布局为PatternLayout 自定义级别,需要使用ConversionPattern指定输出格式
log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p (%c:%L) - %m%n
# 对目标LOGFILE进行设置(与Logger对应)
# 设置日志目标类型为org.apache.log4j.FileAppender文件类型
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
# 指定输出文件路径,相对路径或绝对路径
log4j.appender.LOGFILE.File =./logs/error.log
#日志输出到文件,默认为true
log4j.appender.LOGFILE.Append = true
# 指定输出日志级别
log4j.appender.LOGFILE.Threshold = ERROR
# 是否立即输出,默认值是 true,
log4j.appender.LOGFILE.ImmediateFlush = true
# 设置编码方式
log4j.appender.LOGFILE.Encoding = UTF-8
# 设置日志输出布局方式Layout
log4j.appender.LOGFILE.layout = org.apache.log4j.PatternLayout
# 如果日志输出布局为PatternLayout 自定义级别,需要使用ConversionPattern指定输出格式
log4j.appender.LOGFILE.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
rootLogger部分
总是存在一个rootLogger,即使没有显示配置也是存在的,并且默认输出级别为DEBUG,所有其他的Logger都默认继承自rootLogger
子Logger格式:log4j.logger.childName=INFO,appenderName1,appenderName2...
注意,在设置子Logger后,会造成appender叠加,即子Logger输出一次,父Logger也会输出一次,如果要限制叠加,则需要添加下面的语句:
log4j.additivity.childName=false
Appender部分
日志信息输出目的地类型
类型 | 描述 |
---|---|
org.apache.log4j.ConsoleAppender | 控制台 |
org.apache.log4j.FileAppender | 文件 |
org.apache.log4j.DailyRollingFileAppender | 每天产生一个日志文件 |
org.apache.log4j.RollingFileAppender | 文件大小到达指定尺寸的时候产生一个新的文件 |
org.apache.log4j.WriterAppender | 将日志信息以流格式发送到任意指定的地方 |
ConsoleAppender
控制台常配置属性
-
Threshold: 指定日志消息的输出最低日记级别。
-
ImmediateFlush: 默认值是true,意谓着所有的消息都会被立即输出。
-
Target:指定输出控制台,默认情况下是System.out
FileAppender
文件的常配置属性
-
Threshold:指定日志消息的输出最低日记级别。
-
ImmediateFlush:默认值是true,意谓着所有的消息都会被立即输出。
-
File:指定消息输出的文件路径。
-
Append:默认值是true,即将消息增加到指定文件中,false指将消息覆盖指定的文件内容。
RollingFileAppender
的常配置属性
-
Threshold:指定日志消息的输出最低日记级别。
-
ImmediateFlush:默认值是true,意谓着所有的消息都会被立即输出。
-
File:指定消息输出到mylog.txt文件。
-
Append:默认值是true,即将消息增加到指定文件中,false指将消息覆盖指定的文件内容。
-
MaxFileSize:后缀可以是KB, MB 或者是 GB. 在日志文件到达该大小时,将会自动滚动,即将原来的内容移到mylog.log.1文件。
-
MaxBackupIndex=2:指定可以产生的滚动文件的最大数。
Layout部分
每一个Appender都会指定一个布局,布局的类型有如下:
类型 | 描述 |
---|---|
org.apache.log4j.HTMLLayout | 以HTML表格形式布局 |
org.apache.log4j.PatternLayout | 可以灵活地指定布局模式 |
org.apache.log4j.SimpleLayout | 包含日志信息的级别和信息字符串 |
org.apache.log4j.TTCCLayout | 包含日志产生的时间、线程、类别等等信息 |
其中PatternLayout
类型格式为自定义的,指定此类型需要设置ConversionPattern属性,如:
log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n
ConversionPattern配置属性
符号 | 描述 |
---|---|
%p | 输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL |
%d | 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy MM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921 |
%r | 输出自应用启动到输出该log信息耗费的毫秒数 |
%c | 输出日志信息所属的类目,通常就是所在类的全名 |
%t | 输出产生该日志事件的线程名 |
%l | 输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。 |
%x | 输出和当前线程相关联的NDC(嵌套诊断环境) |
%F | 输出日志消息产生时所在的文件名称 |
%L | 输出代码中的行号 |
%m | 输出代码中指定的消息,产生的日志具体信息 |
%n | 输出一个回车换行符,Windows平台为”\r\n",Unix平台为"\n"输出日志信息换行 |
%% | 输出一个"%“字符 |
- | "-"号指定左对齐,位于%与字母间,例:%-d |
. | "."号指定字符长度,超过设置长度则将左边多余的截掉,例%.20c |
(3)代码中使用
import org.apache.log4j.Logger;
public class TestLog4j {
public static void main(String[] args) {
// 获取此类的logger,getLogger()中写此类
final Logger logger = Logger.getLogger(TestLog4j.class);
// 获取在配置文件中自定义的appender,即输出目标
final Logger saveUserLog = Logger.getLogger("Console");
// 手动设置日志输出
logger.info("info");
logger.error("error");
saveUserLog.info("张三,男,26岁,北京大学,2018-05-19,学霸");
}
}
总结:用户使用Logger来进行日志记录,Logger持有若干个Appender,日志的输出操作是由Appender完成的,它会将日志内容输出到指定位置(日志文件、控制台等)。Appender在输出日志时会使用Layout,将输出内容进行排版。
JUL日志框架
-
JUL
全称Java Util Logging
,是Java
原生的日志框架,使用时不需要另外引入第三方类库,相对于其他日志框架来说其特点是使用方便,能够在小型应用中灵活应用。 -
JUL是 Sun 公司于 2002 年 5 月正式发布的。它是自 J2SE 1.4 版本开始提供的一个新的应用程序接口,需 JDK1.4 版本以上才能支持。
-
JUL
日志框架使用的频率并不高,但开发时难免会涉及,最好能理解。
JUL日志组件
组件 | 描述 |
---|---|
Logger(记录器) | 用于记录系统或应用程序的消息,是访问日志系统的入口程序 |
Handler(处理器) | 也称Appender,从记录器获取日志消息并输出,决定日志记录最终的输出位置 |
Filters(过滤器) | 用于对记录的内容提供细粒度控制,超出日志级别提供的控制 |
Formatter(格式) | 提供对日志记录格式化的支持,决定日志记录最终的输出形式,相当于Layout |
JUL使用的日志级别
级别 | 描述 |
---|---|
OFF | 关闭所有消息的日志记录 |
SEVERE | 错误信息 |
WARNING | 警告信息 |
INFO | 默认信息 |
CONFIG | 配置信息 |
FINE | 较详细 |
FINER | 详细 |
FINEST | 超详细 |
ALL | 启用所有消息的日志记录 |
日志优先级别从高到低顺序为: OFF > SEVERE > WARNING > INFO > CONFIG > FINE > FINER > FINEST > ALL
JUL使用
JUL在Java中有系统默认的配置文件,当开发人员没有设置配置文件时,则会使用系统默认的配置文件。
使用步骤(无配置文件):
1、导入java.util.logging.Logger
包
2、获取当前类的日志记录器Logger
3、调用日志的相关方法,进行日志信息的输出
示例:
import org.junit.Test;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JULTest {
@Test
public void test01() {
// 引入当前类的全路径字符串获取日志记录器
Logger logger = Logger.getLogger("com.jul.JulTest");
// 注:在获取logger之后,可使用logger的API接口进行日志配置
// 例如:logger.setLevel(level.INFO) 配置日志显示的最低级别
// 此方式代码耦合度过高,代码复杂,不建议使用
String name = "张三";
int age = 23;
// 打印日志信息,对于日志的输出有两种方式
// 1、直接调用日志级别的相关方法,方法中传递日志输出信息
logger.info("学生姓名:" + name + ",学生年龄:" + age");
// 2、调用log方法,通过Level类型定义日志级别参数,以及搭配日志输出信息的参数
logger.log(Level.INFO, "学生姓名:" + name + ",学生年龄:" + age");
// 拼接字符串一般不建议使用,性能差,效率低,推荐使用占位符进行输出
logger.log(Level.INFO, "学生姓名:{0},学生年龄:{1}", new Object[]{name, age});
}
}
使用步骤(有配置文件):
1、在resource目录下配置logging.properties
示例:
# RootLogger的日志级别(默认INFO),所有的Handler都受限于此日志级别,Handler的日志级别可以比RootLogger的日志级别高
.level=ALL
# RootLogger默认的处理器,可以配置多个,所有非手动解除父日志的子日志都将使用这些处理器
handlers=java.util.logging.ConsoleHandler, java.util.logging.FileHandler
# ConsoleHandler控制台输出处理器配置
# 指定ConsoleHandler默认日志级别
java.util.logging.ConsoleHandler.level=ALL
java.util.logging.ConsoleHandler.encoding=UTF-8
# FileHandler文件输出处理器配置
# 指定FileHandler默认日志级别
java.util.logging.FileHandler.level=INFO
# 日志文件输出路径
java.util.logging.FileHandler.pattern=/dylan%u.log
# 单个日志文件大小,单位是bit,1024bit即为1kb
java.util.logging.FileHandler.limit=1024*1024*10
# 日志文件数量,如果数量为2,则会生成dylan.log.0文件和dylan.log.1文件,总容量为: (limit * count)bit
java.util.logging.FileHandler.count=1
# FileHandler持有的最大并发锁数
java.util.logging.FileHandler.maxLocks=100
# 指定要使用的Formatter类的名称,FileHandler默认使用的是XMLFormatter
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
# 涉及中文日志就最好加上编码集
java.util.logging.FileHandler.encoding=UTF-8
# 是否以追加方式添加日志内容
java.util.logging.FileHandler.append=true
# SimpleFormatter的输出格式配置
java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n
2、在代码中读取配置文件
3、获取LogManager
4、获取当前类的日志记录器Logger
5、调用日志的相关方法,进行日志信息的输出
示例:
import org.apache.log4j.Logger;
public class TestLog4j {
@Test
public void testUserDefined() throws IOException {
// 1.读取配置文件
InputStream in = new FileInputStream("logging.properties");
// 2.获取LogManager日志管理器
final LogManager logManager = LogManager.getLogManager();
// 3.日志管理器读取自定义配置文件
logManager.readConfiguration(in);
// 4.输出日志信息
Logger logger = Logger.getLogger("com.jul.TestLog4j");
logger.warning("warning:警告信息");
logger.info("info:默认信息");
}
JCL 日志门面
Jakarta Commons-logging(JCL)是apache最早提供的日志门面。它为多种日志框架提供一个通用的日志接口,并通过动态查找机制,在程序运行时自动找出真正使用的日志组件。用户可通过配置,自由选择第三方的日志组件作为日志的具体实现。
使用日志级别
日志级别 | 日志描述 |
---|---|
OFF | 关闭:最高级别,不输出日志 |
FATAL | 致命:输出非常严重的可能会导致应用程序终止的错误。 |
ERROR | 错误:严重错误,主要是程序出错。 |
WARN | 警告:输出可能潜在的危险状况。 |
INFO | 信息:输出应用运行过程的详细信息。 |
DEBUG | 调试:输出更细致的对调试应用有用的信息。 |
TRACE | 跟踪:输出更细致的程序运行轨迹。 |
ALL | 所有:输出所有级别信息。 |
日志优先级别从高到低顺序为: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL
apache建议使用4级,即 ERROR、WARN、INFO、DEBUG
使用步骤
(1)导入common-logging
依赖
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
(2)在项目的src/main/resource目录下创建common-logging.properties
配置:
// 指定使用的日志框架
org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
(3)在程序中使用logger开发
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class CommonsTest {
//使用common-logger的logFactory.getLog()方法获取当前类的logger
private final static Log logger = LogFactory.getLog(CommonsTest.class);
public static void main(String[] args) {
//使用logger输出日志
logger.debug("DEBUG ...");
logger.info("INFO ...");
logger.error("ERROR ...");
logger.warn("WARN...");
}
}
commons.logging
原理
JCL
有两个重要的抽象类:Log
( 基本记录器 ) 和 LogFactory
( 负责创建 Log 实例 )
Log:包含着多个日志框架的默认实现类
实现类 | 对应日志框架 |
---|---|
org.apache.commons.logging.impl.Jdk14Logger | JUL |
org.apache.commons.logging.impl.Log4JLogger | Log4J |
org.apache.commons.logging.impl.LogKitLogger | avalon-Logkit |
org.apache.commons.logging.impl.SimpleLog | common-logging自带 |
org.apache.commons.logging.impl.NoOpLog | common-logging自带 |
这些实现类中只有common-logging
自带的SimpleLog和NoOpLog有实际的log实现,其他的实现类需要使用对应的日志组件进行具体的实现。
LogFactory:顾名思义,是log的工厂,负责使用动态查找机制进行log实例的获取。
其查找步骤如下(找到则停):
1、首先在classpath下寻找commons-logging.properties文件中配置的log,如存在,则使用;
2、查看classpath中是否有Log4j的包,如果发现,则自动使用Log4j作为日志实现类;
3、使用JDK自身的日志实现类java.util.logging.Logger;
4、使用commons-logging自己提供的简单的日志实现类SimpleLog;
文件commons-logging.properties
的配置(可选)
一句话!
// 指定使用的日志框架
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
由于commons-logging并没有进行具体的日志实现,更多的时候用作适配器使用,即插即拔,所以并不需要太多的配置,详细的配置由使用的日志组件进行配置。
当然,如果使用的是commons-logging自带的SimpleLog进行日志实现,则需要进行稍微多一点的配置。
SimpleLog
这是一个很简单的日志输出实现类,它只能控制台输出,提供了以下可定制的属性
// 是否输出类的全路径名,如:com.qinxin.User,默认为false
org.apache.commons.logging.simplelog.showlogname
// 是否输出类的短路径名,如:User,默认为true且与showlogname不能共存
org.apache.commons.logging.simplelog.showShortLogname
// 是否输出时间,默认不输出
org.apache.commons.logging.simplelog.showdatetime
// 输出的时间格式,默认为yyyy/MM/dd HH:mm:ss:SSS zzz
org.apache.commons.logging.simplelog.dateTimeFormat
NoOpLog
这个实现类什么都不做,所有的方法均为空
附言:commons-logging一般不使用自带的日志框架,最多的使用是搭配log4j进行开发,此搭配也是主流选择之一。
SLF4J简单的日志门面
SLF4J(Simple logging Facade for Java)
与JCL(commons-logging)
一样,并不是一个真正的日志实现,而是一个抽象层,具体实现都交由日志框架完成。它封装有各种Logging所需要使用到的api,每个logging对应一种日志框架,用户在使用时只需要将使用的日志框架进行绑定即可。
SLF4J运行机制
如前面所言,SLF4J
是一个日志门面,也是一种规范。它提供一个供各种日志框架作具体实现的接口SLF4JServiceProvider
,各日志框架想要绑定SLF4J
并使用,就必须对接口SLF4JServiceProvider
进行实现(由日志框架提供方实现,开发者无需担心),之后,SLF4J
会通过SPI机制(Java内置的服务发现机制)对项目类路径下所有实现了SLF4JServiceProvider
接口的实例类进行扫描,获取对应的Logger工厂类。
补充说明:
-
如果实例列表中存在1个或多个实例,只获取第一个实例,即配置多个日志框架,只会使用最先被加载的那一个。如果一个日志框架都不存在,则打印警告信息。
-
SPI机制要求日志框架提供方实现
SLF4JServiceProvider
,但多数框架并不会想LogBack一样主动实现该接口,所以出现了slf4j-log4j12
、slf4j-jdk14
、slf4j-simple
等项目,也只有引入这些依赖,SPI机制才会扫描到。
SLF4J使用
1、在maven项目中引入依赖
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.10</version>
</dependency>
2、在classpath路径下配置logback.xml
(详细配置请看LogBack篇)
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="Console"/>
</root>
</configuration>
3、在代码中使用
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
logger.info("这是info信息");
logger.warn("这是warn信息");
logger.debug("这是debug信息");
}
}
SLF4j的优点
-
即插即拔,可快速进行日志框架的替换
-
支持”{}“占位符功能。字符串拼接一直一来都被人所诟病的存在,每一次拼接并非是在原有的字符串上增加,而是重新创建一个String类型的字符串,极其消耗性能和空间。而使用占位符功能不仅降低了你代码中字符串连接次数,而且还节省了新建的String对象。这也是很多人喜欢使用SLF4J而不是commons-logging的原因。
LogBack日志框架
LogBack与SLF4j是同一作者开发,可以说,LogBack是为SLF4J而生,它比log4j更小、更快、更灵活。
LogBack的三个模块:
-
logback-core:核心代码模块
-
logback-classic:日志模块,完整实现了 SLF4J API
-
logback-access:配合Servlet容器,提供 http 访问日志功能
在别人的项目中,你可能看到别人只引入了logback-classic
依赖,而没有引入核心模块,那是因为通常引入logback-classic
的依赖,便可自动引入logback-core
,但保险起见,建议引入两者或者全部。
SpringBoot默认集成了logback
和slf4j
,因此无需专门引入便可进行直接使用,若不放心,也可显式添加配置。
LogBack的日志级别
日志级别 | 日志描述 |
---|---|
OFF | 关闭:最高级别,不输出日志 |
FATAL | 致命:输出非常严重的可能会导致应用程序终止的错误。 |
ERROR | 错误:严重错误,主要是程序出错。 |
WARN | 警告:输出可能潜在的危险状况。 |
INFO | 信息:输出应用运行过程的详细信息。 |
DEBUG | 调试:输出更细致的对调试应用有用的信息。 |
TRACE | 跟踪:输出更细致的程序运行轨迹。 |
ALL | 所有:输出所有级别信息。 |
日志优先级别从高到低顺序为: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL
LogBack的使用
1、在maven项目中导入依赖
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.10</version>
</dependency>
2、在项目类路径中配置LogBack.xml(或者LogBack.groovy)
配置样例:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true" scan="true" scanPeriod="1 seconds">
<contextName>logback</contextName>
<!--定义参数,可以通过${app.name}使用-->
<property name="app.name" value="logback_test"/>
<!--root是默认的logger,level指定输出日志级别是trace-->
<root level="trace">
<!--绑定两个appender,日志通这两个appender配置进行输出-->
<appender-ref ref="console"/>
<appender-ref ref="file"/>
</root>
<!--自定义Logger,输出日志级别为warn,身为子Logger,它会继承root节点的appender-->
<logger name="logbackTest" level="warn"/>
<!--自定义Logger,设置了appender,同时继承root的appender,会导致一条日志在控制台输出两次的情况-->
<!--可通过设置additivity指定不使用rootLogger配置的appender进行输出-->
<logger name="mytest" level="info" additivity="false">
<appender-ref ref="stdout"/>
</logger>
<!--设置root中定义的appender,用class属性指定日志为ConsoleAppender控制台输出日志-->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<!--定义过滤器,要求只打印比此日志级别更高的日志-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<!-- encoder 默认配置为PatternLayoutEncoder自定义布局,需要配置格式 -->
<encoder>
<pattern>%d [%thread] %-5level %logger{36} [%file : %line] - %msg%n</pattern>
</encoder>
</appender>
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--定义日志输出的相对路径/绝对路径,${app.name}为引用properties定义的参数-->
<file>/logs/${app.name}.log</file>
<!--定义日志滚动的策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--定义文件滚动时的文件名的格式-->
<fileNamePattern>/logs/${app.name}.%d{yyyy-MM-dd.HH}.log.gz
</fileNamePattern>
<!--设置60天的时间周期,日志量最大20GB-->
<maxHistory>60</maxHistory>
<!-- 该属性在 1.1.6版本后 才开始支持-->
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<!--每个日志文件最大100MB-->
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
<!--定义输出格式-->
<encoder>
<pattern>%d [%thread] %-5level %logger{36} [%file : %line] - %msg%n</pattern>
</encoder>
</appender>
</configuration>
每次看到xml配置都很头痛,但日志配置总会遵循着Logger、Appender、Layout进行开展。
3、在代码中使用
public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
logger.info("这是info信息");
logger.warn("这是warn信息");
logger.debug("这是debug信息");
}
}
Log4j2日志框架
log4j2是log4j的升级版,继logBack之后出现,拥有着更高的性能和日志吞吐量,且引进有新的技术(无锁异步等),并且配置更加灵活。目前最火热的搭配为SLF4J+Log4j2日志开发。
Log4j2中文文档:Log4j2 中文文档 - Log4j2 2.x Manual | Docs4dev
Log4j2的日志级别
日志级别 | 日志描述 |
---|---|
OFF | 关闭:最高级别,不输出日志 |
FATAL | 致命:输出非常严重的可能会导致应用程序终止的错误。 |
ERROR | 错误:严重错误,主要是程序出错。 |
WARN | 警告:输出可能潜在的危险状况。 |
INFO | 信息:输出应用运行过程的详细信息。 |
DEBUG | 调试:输出更细致的对调试应用有用的信息。 |
TRACE | 跟踪:输出更细致的程序运行轨迹。 |
ALL | 所有:输出所有级别信息。 |
日志优先级别从高到低顺序为: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL
log4j2配置
log4j2配置文件可有yml、properties、xml、json等格式,常见使用xml格式。
多个配置文件存在时,加载的优先级为 properties
> yaml
> json
> xml
。
配置样例(简洁模式):
<?xml version="1.0" encoding="UTF-8"?>
<!--根元素,status设置应该记录到控制台的内部 Log4j 事件的级别-->
<Configuration status="WARN">
<!--设置全局变量-->
<Properties>
<Property name="date_format" value="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
<Property name="log_path">D:/logs</Property>
</Properties>
<!--设置过滤器-->
<Filter type="ThresholdFilter" level="trace"/>
<!--设置输出目标-->
<Appenders>
<!--配置一个名为Console的控制台类型appender-->
<Console name="Console" target="SYSTEM_OUT">
<!--设置输出格式,引用全局变量-->
<PatternLayout pattern="${date_format}"/>
<!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
</Console>
<!--配置一个名为File的文件类型appender-->
<File name="File" fileName="${log_path}/xml_config.log">
<PatternLayout pattern="%d %p %c{1.} [%t] %m%n"/>
</File>
<!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
<RollingFile name="RollingFileError"
fileName="${log_path}/error.log"
filePattern="${log_path}/$${date:yyyy-MM}/${FILE_NAME}-%d{yyyy-MM-dd}-ERROR_%i.log.gz">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<!--interval属性用来指定多久滚动一次,默认是1小时-->
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="20MB"/>
</Policies>
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
<DefaultRolloverStrategy max="15"/>
</RollingFile>
<!--配置名为database的数据库类型的appender-->
<JDBC name="databaseAppender" tableName="dbo.application_log">
<DataSource jndiName="java:/comp/env/jdbc/LoggingDataSource" />
<Column name="eventDate" isEventTimestamp="true" />
<Column name="level" pattern="%level" />
<Column name="logger" pattern="%logger" />
<Column name="message" pattern="%message" />
<Column name="exception" pattern="%ex{full}" />
</JDBC>
</Appenders>
<Loggers>
<!--配置根Logger,设置日志级别为info-->
<Root level="info">
<!--使用在Appenders中定义的名为Console的appender-->
<AppenderRef ref="Console"/>
</Root>
<!--配置子Logger,-->
<Logger name="com.qinxin.Log4j2Test" level="debug" additivity="false">
<AppenderRef ref="File"
</Logger>
</Loggers>
</Configuration>
各节点层级关系:
Configuration <!--配置文件根节点-->
-
Name <!--日志项目名称(可选)-->
-
Properties <!--全局配置参数(可选)-->
-
Filter <!--过滤器(可选)-->
-
Appenders <!--输出目标-->
-
Async <!--设置异步Appender(不推荐,推荐使用异步Logger)-->
-
AppenderRef <!--绑定Appender-->
-
-
ConsoleAppender <!--在控制台输出的Appender-->
-
PatternLayout <!--自定义输出格式-->
-
ThresholdFilter
-
-
FileAppender <!--在文件输出的Appender-->
-
PatternLayout <!--自定义输出格式-->
-
ThresholdFilter
-
-
JDBCAppender <!--在数据库输出的Appender-->
-
DataSource
-
ConnectionFactory
-
DriverManager
-
PoolingDriver
-
-
HTTPAppender <!--通过 HTTP 发送日志的Appender-->
-
NoSqlAppender <!--将日志事件写入 NoSQL 数据库的Appender,当前存在 MongoDB 和 Apache CouchDB 的提供程序实现-->
-
-
Loggers
-
Root <!--父Logger-->
-
Logger <!--自定义子logger,会继承父logger的appender-->
-
Configuration属性
Attribute Name | Description |
---|---|
advertiser | (可选)advertiser 插件名称,将用于广告各个 FileAppender 或 SocketAppender 配置。提供的唯一 advertiser 插件是“ multicastdns”。 |
dest | stderr 的“ err”,stdout 的“ out”,文件路径或 URL。 |
monitorInterval | 在检查文件配置是否更改之前必须经过的最短时间(以秒为单位)。 |
name | 配置的名称。 |
packages | 用逗号分隔的软件包名称列表,用于搜索插件。每个类加载器仅加载一次插件,因此更改此值可能对重新配置没有任何影响。 |
schema | 标识类加载器的位置,该位置用于定位 XML 模式以用于验证配置。仅在 strict 设置为 true 时有效。如果未设置,则不会进行任何模式验证。 |
shutdownHook | 指定在 JVM 关闭时 Log4j 是否应自动关闭。默认情况下,关闭钩子是启用的,但可以通过将此属性设置为“禁用”来禁用 |
shutdownTimeout | 指定关闭 JVM 时将关闭多少毫秒的附加程序和后台任务。默认值为零,这意味着每个追加程序都使用其默认超时,并且不 await 后台任务。并非所有的附加程序都将遵守此规则,这只是提示,而不是绝对的保证,关闭过程将不需要更长的时间。将此值设置得太低会增加丢失尚未写入最终目标的未决日志事件的风险。参见LoggerContext.stop(long, java.util.concurrent.TimeUnit)。 (如果 shutdownHook 设置为“禁用”,则不使用.) |
status | 应该记录到控制台的内部 Log4j 事件的级别。此属性的有效值为“ trace”,“ debug”,“ info”,“ warn”,“ error”和“ fatal”。 Log4j 会将有关初始化,过渡和其他内部操作的详细信息记录到状态 Logger 中。如果需要对 log4j 进行故障排除,设置 status =“ trace”是您可以使用的首批工具之一。 |
strict | 启用严格 XML 格式的使用。 JSON 配置中不支持。 |
verbose | 在加载插件时启用诊断信息。 |
ConsoleAppender属性:
Parameter Name | Type | Description |
---|---|---|
filter | Filter | 确定事件是否应由此 Appender 处理的过滤器。通过使用 CompositeFilter,可以使用多个过滤器。 |
layout | Layout | 用于格式化 LogEvent 的 Layout。如果未提供任何布局,则将使用默认图案布局“%m%n”。 |
follow | boolean | 标识附加程序是否通过配置后进行的 System.setOut 或 System.setErr 兑现 System.out 或 System.err 的重新分配。请注意,在 Windows 上,follow 属性不能与 Jansi 一起使用。不能直接使用。 |
direct | boolean | 直接写入 java.io.FileDescriptor 并绕过 java.lang.System.out/.err。将输出重定向到文件或其他进程时,最多可以使性能提高 10 倍。在 Windows 上不能与 Jansi 一起使用。不能与关注一起使用。输出将不遵守 java.lang.System.setOut()/。setErr(),并且可能与多线程应用程序中 java.lang.System.out/.err 的其他输出交织在一起。自 2.6.2 起新增。请注意,这是一个新增功能,到目前为止,仅在 Linux 和 Windows 上使用 Oracle JVM 进行了测试。 |
name | String | Appender的名字。 |
ignoreExceptions | boolean | 默认值为 true,导致在追加事件时遇到的异常会在内部记录下来,然后被忽略。设置为 false 时,异常将传播到调用方。当将此 Appender 封装在FailoverAppender中时,必须将其设置为 false。 |
target | String | “ SYSTEM_OUT”或“ SYSTEM_ERR”。默认值为“ SYSTEM_OUT”。 |
FileAppender属性:
Parameter Name | Type | Description |
---|---|---|
append | boolean | 如果为 true-默认值,记录将附加到文件末尾。设置为 false 时,将在写入新记录之前清除文件。 |
bufferedIO | boolean | 如果为 true-默认值,则记录将被写入缓冲区,并且当缓冲区已满或(如果设置了 InstantFlush 时)记录被写入时,数据将被写入磁盘。文件锁定不能与 bufferedIO 一起使用。性能测试表明,即使启用了 InstantFlush,使用缓冲 I/O 也会显着提高性能。 |
bufferSize | int | 当 bufferedIO 为 true 时,这是缓冲区大小,默认值为 8192 字节。 |
createOnDemand | boolean | 附加器按需创建文件。仅当日志事件通过所有过滤器并将其路由到此附加程序时,附加程序才创建文件。默认为 false。 |
filter | Filter | 确定事件是否应由此 Appender 处理的过滤器。通过使用 CompositeFilter,可以使用多个过滤器。 |
fileName | String | 要写入的文件名。如果该文件或其任何父目录不存在,则将创建它们。 |
immediateFlush | boolean | 设置为 true-默认值时,每次写操作后都会进行刷新。这将确保将数据写入磁盘,但可能会影响性能。 |
JDBCAppender属性:
Parameter Name | Type | Description |
---|---|---|
name | String | 需要。Appender的名字。 |
ignoreExceptions | boolean | 默认值为 true,导致在追加事件时遇到的异常会在内部记录下来,然后被忽略。设置为 false 时,异常将传播到调用方。当将此 Appender 封装在FailoverAppender中时,必须将其设置为 false。 |
filter | Filter | 确定事件是否应由此 Appender 处理的过滤器。通过使用 CompositeFilter,可以使用多个过滤器。 |
bufferSize | int | 如果大于 0 的整数,这将导致附加程序缓冲日志事件并在缓冲区达到此大小时刷新。 |
connectionSource | ConnectionSource | 需要。应从中检索数据库连接的连接源。 |
tableName | String | 需要。要向其中插入日志事件的数据库表的名称。 |
columnConfigs | ColumnConfig[] | 必需(和/或 columnMappings)。有关应将事件数据日志记录的列的信息以及如何插入该数据的信息。这由多个\ 元素表示。 |
columnMappings | ColumnMapping[] | 必需(和/或 columnConfigs)。列 Map 配置列表。每列必须指定一个列名。每列都可以具有由其完全限定的类名指定的转换类型。默认情况下,转换类型为字符串。如果配置的类型与ReadOnlyStringMap/ThreadContextMap或ThreadContextStack分配兼容,则该列将分别用 MDC 或 NDC 填充(这是数据库特定的,他们如何处理插入 Map 或 List 值)。如果配置的类型与 java.util.Date 分配兼容,则日志时间戳记将转换为该配置的日期类型。如果配置的类型与 java.sql.Clob 或 java.sql.NClob 兼容,则格式化事件将分别设置为 Clob 或 NClob(类似于传统的 ColumnConfig 插件)。如果给定了 Literals 属性,则将在 INSERT 查询中按原样使用其值,而不会进行任何转义。否则,指定的布局或图案将转换为配置的类型并存储在该列中。 |
immediateFail | boolean | 默认为false, 设置为 true 时,日志事件将不 await 尝试重新连接,如果 JDBC 资源不可用,日志事件将立即失败。 2.11.2 的新功能 |
reconnectIntervalMillis | long | 默认为5000,如果设置为大于 0 的值,则在发生错误后,JDBCDatabaseManager 将在 await 指定的毫秒数后尝试重新连接到数据库。如果重新连接失败,则将引发异常(如果将 ignoreExceptions 设置为 false,则应用程序可以捕获该异常)。 2.11.2 中的新功能 |
PatternLayout的pattern参数:
符号 | 描述 |
---|---|
%d | 表示时间,默认情况下表示打印完整时间戳 2012-11-02 14:34:02,123 ,可以调整 %d 后面的参数来调整输出的时间格式,格式为%d{HH:mm:ss} |
%p | 表示输出日志的等级,可以使用 %highlight{%p} 来高亮显示日志级别 |
%c | 用来输出类名,默认输出的是完整的包名和类名,%c{1.} 输出包名的首字母和完整类名 |
%t | 表示线程名称 |
%m | 表示日志内容,%M 表示方法名称 |
%n | 表示换行符 |
%L | 表示打印日志的代码行数 |
%msg | 日志文本 |
%-5level | 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0、 |
%logger | 输出logger名称,因为Root Logger没有名称,所以没有输出 |
%F | 输出所在的类文件名,如:Log4j2Test.java |
详细配置信息可查看Log4j2中文文档:Log4j2 中文文档 - Log4j2 2.x Manual | Docs4dev
Maven项目使用
1、引入依赖
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
2、可进行文件配置,也可直接使用(直接使用默认输出级别为Error)
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4j2HelloWorld {
private static final Logger logger = LogManager.getLogger(Log4j2HelloWorld.class);
public static void main(String[] args) {
logger.error("This is a error");
}
}
SpringBoot整合Log4j2
1、引入依赖
由于SpringBoot默认使用SLF4J+LogBack,所以如果要使用Log4j2,就需要从spring-boot-starter-web中去掉spring-boot-starter-logging依赖,同时引入Log4j2的依赖jar包。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 去掉默认配置 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入log4j2依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
2、定义配置文件(不做展示)
3、在代码中使用
public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
logger.info("这是info信息");
logger.warn("这是warn信息");
logger.debug("这是debug信息");
}
}