一、简介
JUL:
JUL全称Java util Logging是java原生的日志框架,使用时不需要另外引用第三方类库,相对其他日志框架使用方便,学习简单,能够在小型应用中灵活使用。
log4j:
Log4j是Apache下的一款开源的日志框架,通过在项目中使用 Log4J,我们可以控制日志信息输出到控制台、文件、甚至是数据库中。我们可以控制每一条日志的输出格式,通过定义日志的输出级别,可以更灵活的控制日志的输出过程。方便项目的调试。
Logback:
Logback是由log4j创始人设计的另一个开源日志组件,性能比log4j要好。
log4j2:
Apache Log4j 2是对Log4j的升级版,参考了logback的一些优秀的设计,并且修复了一些问题,因此带来了一些重大的提升。
日志门面:
借鉴JDBC的思想,为日志系统也提供一套门面,那么我们就可以面向这些接口规范来开发,避免了直接依赖具体的日志框架。这样我们的系统在日志中,就存在了日志的门面和日志的实现。
现有的日志框架体系
二、日志门面
当我们的系统变的更加复杂的时候,我们的日志就容易发生混乱。随着系统开发的进行,可能会更新不同的日志框架,造成当前系统中存在不同的日志依赖,让我们难以统一的管理和控制。就算我们强制要求所有的模块使用相同的日志框架,系统中也难以避免使用其他类似spring,mybatis等其他的第三方框架,它们依赖于我们规定不同的日志框架,而且他们自身的日志系统就有着不一致性,依然会出来日志体系的混乱。
所以我们需要借鉴JDBC的思想,为日志系统也提供一套门面,那么我们就可以面向这些接口规范来开发,避免了直接依赖具体的日志框架。这样我们的系统在日志中,就存在了日志的门面和日志的实现。
常见的日志门面 :
JCL、slf4j
常见的日志实现:
JUL、log4j、logback、log4j2
日志框架出现的历史顺序:
log4j —> JUL—> JCL—> slf4j —> logback —> log4j2
日志门面和日志实现的关系:
三、Spring Boot 使用Log4j2进行日志输出
同步日志
1、排除logback日志、导入log4j2依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <!--排除 logback 日志实现--> <exclusion> <artifactId>spring-boot-starter-logging</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> <!--使用 log4j2 的日志启动器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency>
2、导入自定义log4j2.xml配置文件
即可实现采用log4j2的配置方式,进行日志的输出。
3.基础log4j2.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="warn" packages="com.lydms.common"> <Appenders> <Console name="console" target="SYSTEM_OUT"> <PatternLayout pattern="[%d %p %t %c] %m%n"/> </Console> <RollingRandomAccessFile name="serverlog" fileName="logs/sys.log" filePattern="logs/sys/sys-%d{yyyyMMdd}-%i.log"> <PatternLayout> <Pattern>[%d %p %t %c] %m%n</Pattern> </PatternLayout> <Policies> <SizeBasedTriggeringPolicy size="500 MB"/> </Policies> </RollingRandomAccessFile> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="console" /> <AppenderRef ref="serverlog"/> </Root> </Loggers> </Configuration>
异步日志
1、排除logback日志、导入log4j2依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <!--排除 logback 日志实现--> <exclusion> <artifactId>spring-boot-starter-logging</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> <!--使用 log4j2 的日志启动器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> <!--异步日志依赖--> <dependency> <groupId>com.lmax</groupId> <artifactId>disruptor</artifactId> <version>3.3.4</version> </dependency>
2、导入自定义log4j2.xml配置文件
即可实现采用log4j2的配置方式,进行日志的输出。
3.基础log4j2.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="warn" packages="com.lydms.common"> <Appenders> <Console name="console" target="SYSTEM_OUT"> <PatternLayout pattern="[%d %p %t %c] %m%n"/> </Console> <RollingRandomAccessFile name="serverlog" fileName="logs/sys.log" filePattern="logs/sys/sys-%d{yyyyMMdd}-%i.log"> <PatternLayout> <Pattern>[%d %p %t %c] %m%n</Pattern> </PatternLayout> <Policies> <SizeBasedTriggeringPolicy size="500 MB"/> </Policies> </RollingRandomAccessFile> </Appenders> <Loggers> <AsyncRoot level="info"> <AppenderRef ref="console" /> <AppenderRef ref="serverlog"/> </AsyncRoot> </Loggers> </Configuration>
四、JCL
1、简介
- 全称为Jakarta Commons Logging,是Apache提供的一个通用日志API。
- 它是为 "所有的Java日志实现"提供一个统一的接口,它自身也提供一个日志的实现,但是功能非常常弱(SimpleLog)。所以一般不会单独使用它。他允许开发人员使用不同的具体日志实现工具: Log4j, Jdk 自带的日志(JUL)
- JCL 有两个基本的抽象类:Log(基本记录器)和LogFactory(负责创建Log实例)。
2、入门案例
代码:
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Test; public class JCLTest { @Test public void testQuick() throws Exception { // 获取 log日志记录器对象 Log log = LogFactory.getLog(JCLTest.class); // 日志记录输出 log.info("hello jcl"); } }
配置文件(使用的是log4j的配置文件)
# 指定 RootLogger 顶级父元素默认配置信息 # 指定日志级别=trace,使用的 apeender 为=console log4j.rootLogger = trace,console # 指定控制台日志输出的 appender log4j.appender.console = org.apache.log4j.ConsoleAppender # 指定消息格式 layout log4j.appender.console.layout = org.apache.log4j.PatternLayout # 指定消息格式的内容 log4j.appender.console.layout.conversionPattern = [%-10p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
pom.xml依赖
<dependencies> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
五、SLF4J
1、简介
简单日志门面(Simple Logging Facade For Java) SLF4J主要是为了给Java日志访问提供一套标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交由其他日志框架,例如log4j和logback等。当然slf4j自己也提供了功能较为简单的实现,但是一般很少用到。对于一般的Java项目而言,日志框架会选择slf4j-api作为门面,配上具体的实现框架(log4j、logback等),中间使用桥接器完成桥接。
SLF4J是目前市面上最流行的日志门面。现在的项目中,基本上都是使用SLF4J作为我们的日志系统。
SLF4J日志门面主要提供两大功能:
- 日志框架的绑定
- 日志框架的桥接
2、SLF4J的简单使用:
import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Slf4jTest { public static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class); // 快速入门 @Test public void test01() throws Exception { // 日志输出 LOGGER.error("error"); LOGGER.warn("wring"); LOGGER.info("info"); // 默认级别 LOGGER.debug("debug"); LOGGER.trace("trace"); // 使用占位符输出日志信息 String name = "lydms"; Integer age = 14; LOGGER.info("用户:{},{}", name, age); // 将系统的异常信息输出 try { int i = 1 / 0; } catch (Exception e) { // e.printStackTrace(); LOGGER.error("出现异常:", e); } } }
3、绑定日志的实现
如前所述,SLF4J支持各种日志框架。SLF4J发行版附带了几个称为“SLF4J绑定”的jar文件,每个绑定对应一个受支持的框架。
使用slf4j的日志绑定流程:
- 添加slf4j-api的依赖。
- 使用slf4j的API在项目中进行统一的日志记录。
- 绑定具体的日志实现框架
- 绑定已经实现了slf4j的日志框架,直接添加对应依赖。
- 绑定没有实现slf4j的日志框架,先添加日志的适配器,再添加实现类的依赖
- Slf4j有且仅有一个日志实现框架的绑定(如果出现多个默认使用第一个依赖日志实现)
slf4j绑定一个框架就行了,如果配置多个的话,就使用第一个。
简单日志实现
<!-- slf4j 日志门面 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency> <!-- slf4j 内置的简单实现--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.21</version> </dependency>
logback日志配置
<!-- slf4j 日志门面 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency> <!--logback 日志实现--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
log4j日志配置
<!-- slf4j 日志门面 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency> <!--绑定 log4j 日志实现,需要导入适配器--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.12</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
不使用任何日志框架
<!-- slf4j 日志门面 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency> <!--nop 日志开关 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>1.7.25</version> </dependency>
JUL日志框架
<!-- slf4j 日志门面 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency> <!--绑定 jul 日志实现,需要导入适配器 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jdk14</artifactId> <version>1.7.25</version> </dependency>
4、如何解决旧日志的升级
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
新版使用 logback日志
迁移的方式: 如果我们要使用SLF4J的桥接器,替换原有的日志框架,那么我们需要做的第一件事情,就是删除掉原有项目中的日志框架的依赖。然后替换成SLF4J提供的桥接器。
- 注释掉过去的log4j,日志依赖,原来代码中的依赖log4j的会报错。
- 采用log4j桥接器进行桥接log4j
- 假如新版的logback日志
<!--logback 日志实现--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <!--配置 log4j 的桥接器--> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> <version>1.7.25</version> </dependency>
假如内部有log4j和log4j桥接器两个依赖。则会报错栈内存溢出( Stack Overflow)
5、日志适配器列举
日志门面(slf4j)
<!-- slf4j 日志门面 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency>
日志实现(logback)
<!--logback 日志实现--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
适配器(slf4j—log4j)
<!--绑定 log4j 日志实现,需要导入适配器--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.12</version> </dependency>
桥接器(log4j—slf4j)
<!--配置 log4j 的桥接器--> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> <version>1.7.25</version> </dependency>
六、JUL日志(INFO级别)
1、简介
JUL全称Java util Logging是java原生的日志框架,使用时不需要另外引用第三方类库,相对其他日志框架使用方便,学习简单,能够在小型应用中灵活使用。
架构简介:
Loggers: 被称为记录器,应用程序通过获取Logger对象,调用其API来来发布日志信息。Logger通常时应用程序访问日志系统的入口程序。
Appenders: 也被称为Handlers,每个Logger都会关联一组Handlers,Logger会将日志交给关联Handlers处理,由Handlers负责将日志做记录。Handlers在此是一个抽象,其具体的实现决定了日志记录的位置可以是控制台、文件、网络上的其他日志服务或操作系统日志等。
Layouts: 也被称为Formatters,它负责对日志事件中的数据进行转换和格式化。Layouts决定了数据在一条日志记录中的最终形式。
Level: 每条日志消息都有一个关联的日志级别。该级别粗略指导了日志消息的重要性和紧迫,我可以将Level和Loggers,Appenders做关联以便于我们过滤消息。
Filters: 过滤器,根据需要定制哪些信息会被记录,哪些信息会被放过。
总结一下:
用户使用Logger来进行日志记录,Logger持有若干个Handler,日志的输出操作是由Handler完成的。在Handler在输出日志前,会经过Filter的过滤,判断哪些日志级别过滤放行哪些拦截,Handler会将日志内容输出到指定位置(日志文件、控制台等)。Handler在输出日志时会使用Layout,将输出内容进行排版。
2、日志级别:
java.util.logging.Level 中定义了日志的级别:
- SEVERE(最高值)
- WARNING
- INFO (默认级别)
- CONFIG
- FINE
- FINER
- FINEST(最低值)
3、入门案例
import org.junit.Test; import java.util.logging.Level; import java.util.logging.Logger; public class JULTest { @Test public void testQuick(){ // 1、获取日志记录器对象 Logger logger = Logger.getLogger("com.lydms.local.log.JULTest"); // 2、日志记录输出 logger.info("hello jul"); // 通用方法进行日志记录 logger.log(Level.INFO,"info log"); // 占位符方式,输出日志值 String name="LiLei"; Integer age= 34; logger.log(Level.INFO,"姓名:{0},年龄:{1}",new Object[]{name,age}); } }
输出结果为:
4、设置日志自定义位置输出、以及输出的日志级别
import org.junit.Test; import java.io.IOException; import java.util.logging.*; public class JULTest { @Test public void testQuick() throws IOException { // 1、获取日志记录器对象 Logger logger = Logger.getLogger("com.lydms.local.log.JULTest"); // 2、关闭系统默认配置 logger.setUseParentHandlers(false); // 自定义配置日志级别 // 3、创建CondolHandler (控制台输出) ConsoleHandler consoleHandler = new ConsoleHandler(); // 创建简单格式转换对象 SimpleFormatter simpleFormatter = new SimpleFormatter(); // 进行关联 consoleHandler.setFormatter(simpleFormatter); logger.addHandler(consoleHandler); // 配置日志具体级别 logger.setLevel(Level.ALL); consoleHandler.setLevel(Level.ALL); // 4、场景FileHandler 文件输出(指定文件输出) FileHandler fileHandler = new FileHandler("logs/jul.log"); // 进行关联 fileHandler.setFormatter(simpleFormatter); logger.addHandler(fileHandler); // 输出的内容 logger.severe("severe"); // 警告级别 logger.warning("warning"); } }
输出结果(控制台和文件都有当前输出的日志)
5、解释日志中父子依赖关系
- 是根据包名进行父子类依赖的。
- 根类是LogManager,所有log都继承LogManager。
@Test public void testLogParent() { Logger logger1 = Logger.getLogger("com.lydms.local.log.JULTest"); Logger logger2 = Logger.getLogger("com.lydms.local.log"); //1、两个对象的地址值是相同的,证明有父子关系 System.out.println(logger1.getParent() == logger2); logger2.setUseParentHandlers(false); //2、设置logger2的日志级别(配置控制台日志输出) ConsoleHandler consoleHandler = new ConsoleHandler(); //创建简单格式转换对象 SimpleFormatter simpleFormatter = new SimpleFormatter(); //进行关联 consoleHandler.setFormatter(simpleFormatter); logger2.addHandler(consoleHandler); //配置日志具体级别 logger2.setLevel(Level.ALL); consoleHandler.setLevel(Level.ALL); logger1.severe("severe"); logger1.info("info"); }
结果:
logger1没有进行配置,照样能够按照logger2的日志设置级别,进行打印日志,表明logger2是logger1的父类。logger1继承logger2的配置。
6、自定义JUL配置
@Test public void testLogProperties() throws IOException { //通过类加载器,读取配置文件 InputStream ins = JULTest.class.getClassLoader().getResourceAsStream("logging.properties"); // 创建LogManager LogManager logManager = LogManager.getLogManager(); // 通过LogManager加载配置文件 logManager.readConfiguration(ins); // 创建日志记录器 Logger logger = Logger.getLogger("com.lydms.local.log"); // 打印日志 logger.severe("severe"); logger.info("info"); }
配置文件:
# 默认的处理器对象(加载日志配置方式_FileHandler(文件输出)、ConsoleHandler(控制台)) handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler # 日志级别 .level= INFO # 文件___日志的名称规范(%h当前用户,%u数字) java.util.logging.FileHandler.pattern = logs/java%u.log # 当前文件,最大存储数量 java.util.logging.FileHandler.limit = 50000 # 文件序号(%u) java.util.logging.FileHandler.count = 1 # formatter消息数据格式 java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter # 控制台___日志级别 java.util.logging.ConsoleHandler.level = INFO # 数据转换器 java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter com.xyz.foo.level = SEVERE
7、配置文件详解
# 默认的处理器对象(RootLogger 顶级父元素指定的默认处理器为:ConsoleHandler) handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler # RootLogger 顶级父元素默认的日志级别为:ALL .level= ALL # 自定义 Logger 使用 com.lydms.handlers = java.util.logging.ConsoleHandler com.lydms.level = CONFIG # 关闭默认配置 com.lydms.useParentHanlders = false # 日志的名称规范(%h当前用户,%u数字) java.util.logging.FileHandler.pattern = logs/java%u.log # 当前文件,最大存储数量 java.util.logging.FileHandler.limit = 50000 # 文件序号(%u) java.util.logging.FileHandler.count = 1 # formatter消息数据格式 java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter # 指定以追加方式添加日志内容 java.util.logging.FileHandler.append = true # 向控制台输出的 handler 对象 # 指定 handler 对象的日志级别 java.util.logging.ConsoleHandler.level = INFO # 指定 handler 对象的日志消息格式对象 java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter # com.xyz.foo.level = SEVERE # 指定 handler 对象的字符集 java.util.logging.ConsoleHandler.encoding = UTF-8 # 指定日志消息格式 java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n
8、自定义日之类使用
配置文件
# 自定义 Logger 使用 com.lydms.handlers = java.util.logging.ConsoleHandler com.lydms.level = CONFIG # 关闭默认配置 com.lydms.useParentHanlders = false
代码:
@Test public void testLogParent() { Logger logger = Logger.getLogger("com.lydms"); logger.severe("severe"); logger.info("info"); }
9、日志原理解析
- 初始化LogManager
- LogManager加载logging.properties配置
- 添加Logger到LogManager
- 从单例LogManager获取Logger
- 设置级别Level,并指定日志记录LogRecord
- Filter提供了日志级别之外更细粒度的控制
- Handler是用来处理日志输出位置
- Formatter是用来格式化LogRecord的
七、Log4j日志
1、简介
Log4j是Apache下的一款开源的日志框架,通过在项目中使用 Log4J,我们可以控制日志信息输出到控制台、文件、甚至是数据库中。我们可以控制每一条日志的输出格式,通过定义日志的输出级别,可以更灵活的控制日志的输出过程。方便项目的调试。
官方网站: http://logging.apache.org/log4j/1.2/
2、入门案例
pom依赖
<!--log4j--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
入门log4j日志信息
import org.apache.log4j.BasicConfigurator; import org.apache.log4j.Logger; import org.junit.Test; public class test { @Test public void testQuick() { // 初始化配置信息 BasicConfigurator.configure(); // 获取日志记录器对象 Logger logger = Logger.getLogger(Log4jTest.class); // 日志记录输出 logger.info("hello world"); // 日志级别 logger.fatal("fatal"); //严重错误,一般会造成系统崩溃并终止运行 logger.error("error"); //错误信息。不会影响系统运行 logger.warn("warn"); //警告信息,可能会发生问题 logger.info("info "); //运行信息,数据连接、网络连接、IO、操作等等 logger.debug("debug"); //调制信息,一般在开发过程中使用,记录程序变量参数传递信息等等 logger.trace("trace"); //追踪信息,记录程序所有的流程信息 } }
开启log4j 内置日志记录
// 开启 log4j 内置日志记录 LogLog.setInternalDebugging(true);
3、log4j 组件
Log4J 主要由 Loggers (日志记录器)、Appenders(输出端)和 Layout(日志格式化器)组成。
- Loggers 控制日志的输出级别与日志是否输出;
- Appenders 指定日志的输出方式(输出到控制台、文件等);
- Layout 控制日志信息的输出格式。
Loggers:
- 日志记录器,负责收集处理日志记录,实例的命名就是类“XX”的full quailied name(类的全限定名),Logger的名字大小写敏感,其命名有继承机制。
- Log4J中有一个特殊的logger叫做“root”,他是所有logger的根,也就意味着其他所有的logger都会直接或者间接地继承自root。root logger可以用Logger.getRootLogger()方法获取。
Appenders:
- Appender 用来指定日志输出到哪个地方,可以同时指定日志的输出目的地。
Log4j 常用的输出目的地。有以下几种:
输出端类型 | 作用 |
ConsoleAppender | 将日志输出到控制台 |
FileAppender | 将日志输出到文件中 |
DailyRollingFileAppender | 将日志输出到一个日志文件,并且每天输出到一个新的文件 |
RollingFileAppender | 将日志信息输出到一个日志文件,并且指定文件的尺寸,当文件大小达到指定尺寸时,会自动把文件改名,同时产生一个新的文件 |
JDBCAppender | 把日志信息保存到数据库中 |
Layouts:
布局器 Layouts用于控制日志输出内容的格式,让我们可以使用各种需要的格式输出日志。
Log4j常用的Layouts:
格式化器类型 | 作用 |
HTMLLayout | 格式化日志输出为HTML表格形式 |
SimpleLayout | 简单的日志输出格式化,打印的日志格式为(info - message) |
PatternLayout | 最强大的格式化期,可以根据自定义格式输出日志,如果没有指定转换格式,就是用默认的转换格式 |
Layout的格式
# %m 输出代码中指定的日志信息 # %p 输出优先级,及 DEBUG、INFO 等 # %n 换行符(Windows平台的换行符为 "\n",Unix 平台为 "\n") # %r 输出自应用启动到输出该 log 信息耗费的毫秒数 # %c 输出打印语句所属的类的全名 # %t 输出产生该日志的线程全名 # %d 输出服务器当前时间,默认为 ISO8601,也可以指定格式,如:%d{yyyy年MM月dd日 HH:mm:ss} # %l 输出日志时间发生的位置,包括类名、线程、及在代码中的行数。如:Test.main(Test.java:10) # %F 输出日志消息产生时所在的文件名称 # %L 输出代码中的行号 # %% 输出一个 "%" 字符
log4j.properties(log4j配置文件)
# 指定 RootLogger 顶级父元素默认配置信息 # 0、指定日志级别=trace,使用的 apeender 为=console log4j.rootLogger = trace,console # 5、自定义 logger 对象设置 log4j.logger.com.lydms = info,console log4j.logger.org.apache = error # 1、指定控制台日志输出的 appender log4j.appender.console = org.apache.log4j.ConsoleAppender # 指定消息格式 layout log4j.appender.console.layout = org.apache.log4j.PatternLayout #log4j.appender.console.layout = org.apache.log4j.HTMLLayout #log4j.appender.console.layout = org.apache.log4j.xml.XMLLayout # 指定消息格式的内容 log4j.appender.console.layout.conversionPattern = [%-10p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n # %m 输出代码中指定的日志信息 # %p 输出优先级,及 DEBUG、INFO 等 # %n 换行符(Windows平台的换行符为 "\n",Unix 平台为 "\n") # %r 输出自应用启动到输出该 log 信息耗费的毫秒数 # %c 输出打印语句所属的类的全名 # %t 输出产生该日志的线程全名 # %d 输出服务器当前时间,默认为 ISO8601,也可以指定格式,如:%d{yyyy年MM月dd日 HH:mm:ss} # %l 输出日志时间发生的位置,包括类名、线程、及在代码中的行数。如:Test.main(Test.java:10) # %F 输出日志消息产生时所在的文件名称 # %L 输出代码中的行号 # %% 输出一个 "%" 字符 # 2、配置日志文件的相关信息(文件路径以及编码格式) # 日志文件输出的 appender 对象 log4j.appender.file = org.apache.log4j.FileAppender # 指定消息格式 layout log4j.appender.file.layout = org.apache.log4j.PatternLayout # 指定消息格式的内容 log4j.appender.file.layout.conversionPattern = [%-10p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n # 指定日志文件保存路径 log4j.appender.file.file = /logs/log4j.log # 指定日志文件的字符集 log4j.appender.file.encoding = UTF-8 # 3.1 、按照文件大小拆分的 appender 对象 # 日志文件输出的 appender 对象 log4j.appender.rollingFile = org.apache.log4j.RollingFileAppender # 指定消息格式 layout log4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout # 指定消息格式的内容 log4j.appender.rollingFile.layout.conversionPattern = [%-10p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n # 指定日志文件保存路径 log4j.appender.rollingFile.file = /logs/log4j.log # 指定日志文件的字符集 log4j.appender.rollingFile.encoding = UTF-8 #总共只会维护10个最大 1 MB 的文件 # 指定日志文件内容的大小(超过以后重新复制到新的文件) log4j.appender.rollingFile.maxFileSize = 1MB # 指定日志文件的数量(超过以后进行覆盖) log4j.appender.rollingFile.maxBackupIndex = 10 # 3.2、按照时间规则拆分的 appender 对象 log4j.appender.dailyFile = org.apache.log4j.DailyRollingFileAppender # 指定消息格式 layout log4j.appender.dailyFile.layout = org.apache.log4j.PatternLayout # 指定消息格式的内容 log4j.appender.dailyFile.layout.conversionPattern = [%-10p]%r %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n # 指定日志文件保存路径 log4j.appender.dailyFile.file = /logs/log4j.log # 指定日志文件的字符集 log4j.appender.dailyFile.encoding = UTF-8 # 指定日期拆分规则 log4j.appender.dailyFile.datePattern = '.'yyyy-MM-dd-HH-mm-ss # 4、向数据库存入日志 mysql log4j.appender.logDB=org.apache.log4j.jdbc.JDBCAppender log4j.appender.logDB.layout=org.apache.log4j.PatternLayout log4j.appender.logDB.Driver=com.mysql.jdbc.Driver log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/test log4j.appender.logDB.User=root log4j.appender.logDB.Password=root log4j.appender.logDB.Sql=INSERT INTO log(project_name,create_date,level,category,file_name,thread_name,line,all_category,message) values('itcast','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')
八、logback
1、简介
Logback是由log4j创始人设计的另一个开源日志组件,性能比log4j要好。
官方网站:https://logback.qos.ch/index.htmlLogback
主要分为三个模块:
- logback-core:其它两个模块的基础模块。
- logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j AP。
- logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能。
后续的日志代码都是通过SLF4J日志门面搭建日志系统,所以在代码是没有区别,主要是通过修改配置文件和pom.xml依赖。
2、 logback入门
pom依赖
<!--slf4j 日志门面--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency> <!--logback 日志实现--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <!--junit单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
代码:
import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LogbackTest { public static final Logger LOGGER = LoggerFactory.getLogger(LogbackTest.class); // 快速入门 @Test public void testQuick() throws Exception { for (int i = 0; i < 10000; i++) { // 日志输出 LOGGER.error("error"); LOGGER.warn("wring"); LOGGER.info("info"); LOGGER.debug("debug");// 默认级别 LOGGER.trace("trace"); } } }
3、logback配置
logback会依次读取以下类型配置文件:
- logback.groovy
- logback-test.xml
- logback.xml
- 如果均不存在会采用默认配置
logback组件之间的关系
- Logger:日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。
- Appender:用于指定日志输出的目的地,目的地可以是控制台、文件、数据库等等。
- Layout:负责把事件转换成字符串,格式化的日志信息的输出。在logback中Layout对象被封装在encoder中。
XML基本配置
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- 配置集中管理属性 我们可以直接改属性的 value 值 格式:${name} --> <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"></property> <!-- 日志输出格式: %-5level %d{yyyy-MM-dd HH:mm:ss.SSS}日期 %c类的完整名称 %M为method %L为行号 %thread线程名称 %m或者%msg为信息 %n换行 --> <!--控制台日志输出的 appender--> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <!--控制输出流对象 默认 System.out 改为 System.err--> <target>System.err</target> <!--日志消息格式配置--> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${pattern}</pattern> </encoder> </appender> <!--root logger 配置--> <root level="ALL"> <appender-ref ref="console"/> </root> </configuration>
日志文件输出格式配置
<!--定义日志文件保存路径属性--> <property name="log_dir" value="/logs"></property> <!--日志文件输出的 appender--> <appender name="file" class="ch.qos.logback.core.FileAppender"> <!--日志文件保存路径--> <file>${log_dir}/logback.log</file> !--日志消息格式配置--> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${pattern}</pattern> </encoder> </appender> <!--root logger 配置--> <root level="ALL"> <appender-ref ref="async"/> </root>
日志HTML格式文件输出
<!--定义日志文件保存路径属性--> <property name="log_dir" value="/logs"></property> <!--html 格式日志文件输出 appender--> <appender name="htmlFile" class="ch.qos.logback.core.FileAppender"> <!--日志文件保存路径--> <file>/logs/logback.html</file> <!--html 消息格式配置--> <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <layout class="ch.qos.logback.classic.html.HTMLLayout"> <pattern>%-5level%d{yyyy-MM-dd HH:mm:ss.SSS}%c%M%L%thread%m</pattern> </layout> </encoder> </appender> <!--root logger 配置--> <root level="ALL"> <appender-ref ref="htmlFile"/> </root>
日志拆分和归档压缩
- 末尾加上 .gz以后,表格是进行归档压缩
- 大小拆分:maxFileSize。
- 时间拆分:
<!--定义日志文件保存路径属性--> <property name="log_dir" value="/logs"></property> <!--日志拆分和归档压缩的 appender 对象--> <appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!--日志文件保存路径--> <file>${log_dir}/roll_logback.log</file> <!--日志消息格式配置--> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${pattern}</pattern> </encoder> <!--指定拆分规则--> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!--按照时间和压缩格式声明拆分的文件名--> <fileNamePattern>${log_dir}/rolling.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern> <!--按照文件大小拆分--> <maxFileSize>1MB</maxFileSize> </rollingPolicy> </appender> <!--root logger 配置--> <root level="ALL"> <appender-ref ref="rollFile"/> </root>
4、过滤器
<level>ERROR</level>
:过滤级别(ERROR)<onMatch>ACCEPT</onMatch>
:大于当前级别(放行)<onMismatch>DENY</onMismatch>
:低于当前级别(拦截)
<appender name="rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!--日志级别过滤器--> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <!--日志过滤规则--> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender>
5、异步日志
源码:
代码:
<!--异步日志--> <appender name="async" class="ch.qos.logback.classic.AsyncAppender"> <!--指定某个具体的 appender--> <appender-ref ref="rollFile"/> </appender> <!--root logger 配置--> <root level="ALL"> <appender-ref ref="console"/> <appender-ref ref="async"/> </root>
自定义logger
- 自定义日志的优先级高于root日志级别。
<!-- 自定义 looger 对象 --> <!-- name:指定哪个目录下,采用自定义日志 --> <!-- additivity="false" 自定义 logger 对象是否继承 rootLogger,root里面配置失效--> <logger name="com.itheima" level="info" additivity="false"> <appender-ref ref="console"/> </logger>
官方提供的log4j.properties转换成logback.xml
https://logback.qos.ch/translator/
九、Log4j2
1、简介
Apache Log4j 2是对Log4j的升级版,参考了logback的一些优秀的设计,并且修复了一些问题,因此带来了一些重大的提升,主要有:
- 异常处理:在logback中,Appender中的异常不会被应用感知到,但是在log4j2中,提供了一些异常处理机制。
- 性能提升:log4j2相较于log4j 和logback都具有很明显的性能提升,后面会有官方测试的数据。
- 自动重载配置:参考了logback的设计,当然会提供自动刷新参数配置,最实用的就是我们在生产上可以动态的修改日志的级别而不需要重启应用。
- 无垃圾机制:log4j2在大部分情况下,都可以使用其设计的一套无垃圾机制,避免频繁的日志收集导致的jvm gc,(对象的复用)。
官网: https://logging.apache.org/log4j/2.x/
目前市面上最主流的日志门面就是SLF4J,虽然Log4j2也是日志门面,因为它的日志实现功能非常强大,性能优越。所以大家一般还是将Log4j2看作是日志的实现,Slf4j + Log4j2应该是未来的大势所趋。
2、入门案例
pom.xml依赖
<!--log4j2日志门面--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.11.1</version> </dependency> <!--log4j2 日志实现--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.11.1</version> </dependency>
代码:
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.Test; public class Log4j2Test { // 定义日志记录器对象 public static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class); // 快速入门 @Test public void testQuick()throws Exception{ // 日志消息输出 LOGGER.fatal("fatal"); LOGGER.error("error"); //默认日志级别 LOGGER.warn("warn"); LOGGER.info("inf"); LOGGER.debug("debug"); LOGGER.trace("trace"); } }
slf4j+log4j2实现 pom依赖
<!--使用slf4j 作为日志门面--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.26</version> </dependency> <!--使用 log4j2 的适配器进行绑定--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.9.1</version> </dependency> <!--log4j2日志门面--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.11.1</version> </dependency> <!--log4j2 日志实现--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.11.1</version> </dependency>
3、log4j2.xml配置信息
- 1、Configuration:根标签。
- 2、properties:集中配置属性及管理。
- 3、Appenders;日志处理器(可配置多个Appender)。
- 4、Loggers:logger定义。
<?xml version="1.0" encoding="UTF-8"?> <!-- 1、Configuration :项目根标签 status="warn" 日志框架本身的输出日志级别 monitorInterval="5" 自动加载配置文件的间隔时间,不低于 5 秒 --> <Configuration status="debug" monitorInterval="5"> <!-- 2、集中配置属性进行管理 使用时通过:${name} --> <properties> <property name="LOG_HOME">/logs</property> </properties> <!-- 3、日志处理--> <Appenders> <!-- 3.1、控制台输出 appender--> <Console name="Console" target="SYSTEM_ERR"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n" /> </Console> <!-- 3.2、日志文件输出 appender--> <File name="file" fileName="${LOG_HOME}/myfile.log"> <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" /> </File> <!-- 3.3、使用随机读写流的日志文件输出 appender,性能提高--> <RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log"> <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" /> </RandomAccessFile> <!-- 3.4、按照一定规则拆分的日志文件的 appender fileName: 先保存在某个文件 filePattern:以一定规则进行日志拆分 --> <RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log" filePattern="/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyy-MM-dd-HH-mm}-%i.log"> <!--日志级别过滤器,level:过滤级别(debug),onMatch:高于级别ACCEPT(放行),onMismatch:低于级别DENY(拦截)--> <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" /> <!--日志消息格式--> <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %msg%n" /> <!--具体的拆分规则--> <Policies> <!--在系统启动时,出发拆分规则,生产一个新的日志文件--> <OnStartupTriggeringPolicy /> <!--按照文件大小拆分,10MB --> <SizeBasedTriggeringPolicy size="10 MB" /> <!--按照时间节点拆分,规则根据filePattern定义的--> <TimeBasedTriggeringPolicy /> </Policies> <!--在同一个目录下,文件的个数限定为 30 个,超过进行覆盖--> <DefaultRolloverStrategy max="30" /> </RollingFile> </Appenders> <!-- 4、logger 定义--> <Loggers> <!--使用 rootLogger 配置 日志级别 level="trace"--> <Root level="trace"> <!--指定日志使用的处理器--> <AppenderRef ref="Console" /> </Root> </Loggers> </Configuration>
4、异步日志
log4j2最大的特点就是异步日志,其性能的提升主要也是从异步日志中受益.
同步日志
同步日志
logEvent调用具体的Appender具体进行实现。Log4j2提供了两种实现日志的方式:
- 一个是通过AsyncAppender(性能提升少);
- 一个是通过AsyncLogger,分别对应前面我们说的Appender组件和Logger组件(主要的性能提升)。
4.1异步配置使用:
pom.xml
<!--异步日志依赖--> <dependency> <groupId>com.lmax</groupId> <artifactId>disruptor</artifactId> <version>3.3.4</version> </dependency>
4.2 异步Appender方式(性能提升少)
<Async name="Async"> <AppenderRef ref="file"/> </Async> <!--使用 rootLogger 配置 日志级别 level="trace"--> <Root level="trace"> <!--使用异步 appender--> <AppenderRef ref="Async" /> </Root>
4.3 AsyncLogger方式
AsyncLogger才是log4j2 的重头戏,也是官方推荐的异步方式。它可以使得调用Logger.log返回的更快。
你可以有两种选择:全局异步和混合异步。
- 全局异步方式:
所有的日志都异步的记录,在配置文件上不用做任何改动,只需要添加一个文件log4j2.component.properties 配置,里面增加如下配置即可;
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
- 混合异步方式:
你可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活。
<!--自定义异步 logger 对象 includeLocation="false" 关闭日志记录的行号信息 additivity="false" 不在继承 rootlogger 对象 --> <AsyncLogger name="com.lydms" level="trace" includeLocation="false" additivity="false"> <AppenderRef ref="Console"/> </AsyncLogger>
异步日志注意事项:
- 如果使用异步日志,AsyncAppender、AsyncLogger和全局日志,不要同时出现。性能会和AsyncAppender一致,降至最低。
- 设置includeLocation=false ,打印位置信息会急剧降低异步日志的性能,比同步日志还要慢。
5、性能
Log4j2在多线程的环境下吞吐量与Log4j和Logback的比较如下图:
- 1)全局使用异步模式;
- 2)部分Logger采用异步模式;
- 3)异步Appender。
性能提升原因: - 异步日志。
- 无垃圾记录
简介:
- 从版本2.6开始,默认情况下Log4j以“无垃圾”模式运行,其中重用对象和缓冲区,并且尽可能不分配临时对象。
- 还有一个“低垃圾”模式,它不是完全无垃圾,但不使用ThreadLocal字段。Log4j 2.6中的无垃圾日志记录部分通过重用ThreadLocal字段中的对象来实现,部分通过在将文本转换为字节时重用缓冲区来实现。
十、Springboot中使用日志
1、内部依赖
pom.xml依赖
<dependency> <artifactId>spring-boot-starter-logging</artifactId> <groupId>org.springframework.boot</groupId> </dependency>
其内部依赖图:
总结:
- springboot 底层默认使用logback作为日志实现。
- 使用了SLF4J作为日志门面。
- 将JUL也转换成slf4j。
- 也可以使用log4j2作为日志门面,但是最终也是通过slf4j调用logback。
- 最终的输出都是采用logback进行日志输出的。
2、使用Springboot默认日志进行输出(logback)
代码:
import org.apache.logging.log4j.LogManager; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class SpringbootLogApplicationTests { // 声明日志记录器对象 public static final Logger LOGGER = LoggerFactory.getLogger(SpringbootLogApplicationTests.class); @Test public void contextLoads() { // 打印日志信息 LOGGER.error("error"); LOGGER.warn("warn"); LOGGER.info("info"); // 默认日志级别 LOGGER.debug("debug"); LOGGER.trace("trace"); // 使用 lo4j2 使用桥接器切换为 slf4j 门面和 logback 日志实现 org.apache.logging.log4j.Logger logger = LogManager.getLogger(SpringbootLogApplicationTests.class); logger.info("log4j2 info"); } }
修改默认的日志级别
# 指定自定义 logger 对象日志级别 logging.level.com.itheima=trace # 指定控制台输出消息格式 logging.pattern.console=[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c [%thread]===== %msg %n # 指定存放日志文件的具体路径 # logging.file=/logs/springboot.log # 指定日志文件存放的目录,默认的文件名 spring.log logging.file.path=/logs/springboot/ # 指定日志文件消息格式 logging.pattern.file=[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c [%thread]===== %msg %n
3、使用自定义日志类型
给类路径下放上每个日志框架自己的配置文件;SpringBoot就不使用默认配置的了
日志框架 | 配置文件 |
Logback | logback-spring.xml , logback.xml |
Log4j2 | log4j2-spring.xml , log4j2.xml |
JUL | logging.properties |
logback.xml:直接就被日志框架识别了
4、采用生产、测试多种日志配置方式
application.properties配置
spring.profiles.active=dev
log4j2.xml配置
<springProfile name="dev">
<springProfile name="pro">
<!--控制台日志输出的 appender--> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <!--控制输出流对象 默认 System.out 改为 System.err--> <target>System.err</target> <!--日志消息格式配置--> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <springProfile name="dev"> <pattern>${pattern}</pattern> </springProfile> <springProfile name="pro"> <pattern>[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] xxxxxxxx %m %n</pattern> </springProfile> </encoder> </appender>
5、使用Log4j2进行日志输出
1、排除logback日志、导入log4j依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <!--排除 logback 日志实现--> <exclusion> <artifactId>spring-boot-starter-logging</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> <!--使用 log4j2 的日志启动器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency>
2、导入自定义log4j2.xml配置文件
即可实现采用log4j2的配置方式,进行日志的输出。
十一、相关地址
1、B站地址
https://www.bilibili.com/video/BV1iJ411H74S?p=1
2、百度网盘地址(包括源码)
http://yun.itheima.com/course/632.html?2012zzp
https://pan.baidu.com/s/1FgyZP7vftO5InMLGzOI-yQ 提取码:ots5