java日志

1 Log4j入门

1.1 Loggers
日志记录器,负责收集处理日志记录,实例的命名就是类 “XX” full quailied name (类的全限定名), Logger的名字大小写敏感,其命名有继承机制:例如: name org.apache.commons logger 会继承 name为 org.apache logger
Log4J 中有一个特殊的 logger 叫做 “root” ,他是所有 logger 的根,也就意味着其他所有的 logger 都会直接 或者间接地继承自root root logger 可以用 Logger.getRootLogger() 方法获取。
但是,自 log4j 1.2 版以来, Logger 类已经取代了 Category 类。对于熟悉早期版本的 log4j 的人来说, Logger 类可以被视为 Category 类的别名。
1.2 Appenders
Appender 用来指定日志输出到哪个地方,可以同时指定日志的输出目的地。 Log4j 常用的输出目的地
有以下几种:
 1.3 Layouts
布局器 Layouts 用于控制日志输出内容的格式,让我们可以使用各种需要的格式输出日志。 Log4j 常用 的Layouts:
1.31 Layout 的格式(PatterLayout)
log4j.properties 配置文件中,我们定义了日志输出级别与输出端,在输出端中分别配置日志的输出格式。
 %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   输出代码中的行号
 %%   输出一个 "%" 字符
% ctFL 相当于 l 就可以包含前几个输出格式


#可以在 % 与字符之间加上修饰符来控制最小宽度、最大宽度和文本的对其方式。如:

%5c 输出category名称,最小宽度是5,category<5,默认的情况下右对齐
%-5c 输出category名称,最小宽度是5,category<5,"-"号指定左对齐,会有空格
%.5c 输出category名称,最大宽度是5,category>5,就会将左边多出的字符截掉,<5不会有空格
%20.30c category名称<20补空格,并且右对齐,>30字符,就从左边交远销出的字符截掉
2 入门案例
1. 建立 maven 工程
2. 添加依赖
<dependencies>
    <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>

3. 代码

public class Log4jTest {
@Test
public void testQuick() throws Exception {
// 初始化系统配置,不需要配置文件, 有配置文件需要注释掉
BasicConfigurator.configure();
// 创建日志记录器对象
Logger logger = Logger.getLogger(Log4jTest.class);
// 日志记录输出
logger.info("hello log4j");
// 日志级别
logger.fatal("fatal"); // 严重错误,一般会造成系统崩溃和终止运行
logger.error("error"); // 错误信息,但不会影响系统运行
logger.warn("warn"); // 警告信息,可能会发生问题
logger.info("info"); // 程序运行信息,数据库的连接、网络、IO操作等
logger.debug("debug"); // 调试信息,一般在开发阶段使用,记录程序的变量、参数等
logger.trace("trace"); // 追踪信息,记录程序的所有流程信息
}
}

4. 配置文件 resources 下面名称为 log4j.properties

# 指定 RootLogger 顶级父元素默认配置信息
# 指定日志级别=trace,使用的 apeender 为=console(console可自定义名称)
log4j.rootLogger=INFO,console,file

# 自定义 logger (继承rootLogger) 对象设置 (日志级别会覆盖rootLogger,appender方式都会起作用)
log4j.logger.com.csi.log = error,mydailyFile

# 指定控制台日志输出的 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

# 日志文件输出的 appender 对象 file 可自定义名
log4j.appender.file = org.apache.log4j.FileAppender
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 = C:/Users/Public/Desktop/logs/log4j.log
# 指定日志文件保存路径
log4j.appender.file.encoding = UTF-8

2.1 FileAppender 的两个子类

FileAppender 默认日志追加到日志文件中,防止过大可进行文件拆分和时间拆分

# 按照文件大小 拆分的 appender 对象   FileAppender  的子类 RollingFileAppender
# 日志文件输出的 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 = C:/Users/15121262356/Desktop/logs/log4j.log
# 指定日志文件的字符集
log4j.appender.rollingFile.encoding = UTF-8
# 指定日志文件内容的大小
log4j.appender.rollingFile.maxFileSize = 1MB
# 指定日志文件的数量  (超过10个 按时间覆盖 比较久远的就给覆盖了)
log4j.appender.rollingFile.maxBackupIndex = 10

# 按照时间规则拆分的 appender 对象
log4j.appender.mydailyFile = org.apache.log4j.DailyRollingFileAppender
# 指定消息格式 layout
log4j.appender.mydailyFile.layout = org.apache.log4j.PatternLayout
# 指定消息格式的内容
log4j.appender.mydailyFile.layout.conversionPattern = [%-10p]%r  %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
# 指定日志文件保存路径
log4j.appender.mydailyFile.file = C:/Users/15121262356/Desktop/logs/log4j.log
# 指定日志文件的字符集
log4j.appender.mydailyFile.encoding = UTF-8
# 指定日期拆分规则
log4j.appender.mydailyFile.datePattern = '.'yyyy-MM-dd-HH-mm-ss

2.2 将日志插入mysql

CREATE TABLE `log` (
`log_id` int(11) NOT NULL AUTO_INCREMENT,
`project_name` varchar(255) DEFAULT NULL COMMENT '目项名',
`create_date` varchar(255) DEFAULT NULL COMMENT '创建时间',
`level` varchar(255) DEFAULT NULL COMMENT '优先级',
`category` varchar(255) DEFAULT NULL COMMENT '所在类的全名',
`file_name` varchar(255) DEFAULT NULL COMMENT '输出日志消息产生时所在的文件名称 ',
`thread_name` varchar(255) DEFAULT NULL COMMENT '日志事件的线程名',
`line` varchar(255) DEFAULT NULL COMMENT '号行',
`all_category` varchar(255) DEFAULT NULL COMMENT '日志事件的发生位置',
`message` varchar(4000) DEFAULT NULL COMMENT '输出代码中指定的消息',
PRIMARY KEY (`log_id`)
);
# 指定 RootLogger 顶级父元素默认配置信息  使用哪个后面加上哪一个
# 指定日志级别=INFO,使用的 apeender 为=console(console可自定义名称)
log4j.rootLogger=INFO,console,myDB

# 自定义 logger (继承rootLogger) 对象设置 (日志级别会覆盖rootLogger,appender方式都会起作用)
log4j.logger.com.csi.log = error,mydailyFile

# 指定控制台日志输出的 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

# 日志文件输出的 appender 对象 file 可自定义名
log4j.appender.file = org.apache.log4j.FileAppender
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 = C:/Users/Public/Desktop/logs/log4j.log
# 指定日志文件保存路径
log4j.appender.file.encoding = UTF-8


#mysql
log4j.appender.myDB=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.myDB.layout=org.apache.log4j.PatternLayout
log4j.appender.myDB.Driver=com.mysql.cj.jdbc.Driver
log4j.appender.myDB.URL=jdbc:mysql://localhost:3306/zk?serverTimezone=Asia/Shanghai
log4j.appender.myDB.User=root
log4j.appender.myDB.Password=root
log4j.appender.myDB.Sql=INSERT log(project_name,create_date,\
  level,category,file_name,thread_name,line,all_category,message) \
  values('zk','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m')


3. 自定义Logger

# 自定义 logger (继承rootLogger) 对象设置 (日志级别会覆盖rootLogger,appender方式都会起作用)
log4j.logger.com.csi.log = error,mydailyFile

2 JCL


全称为Jakarta Commons Logging,是Apache提供的一个通用日志API。

它是为 "所有的Java日志实现"提供一个统一的接口,它自身也提供一个日志的实现,但是功能非常常弱(SimpleLog)。所以一般不会单独使用它。他允许开发人员使用不同的具体日志实现工具: Log4j, Jdk自带的日志(JUL)。

JCL 有两个基本的抽象类:Log(基本记录器) 和LogFactory(负责创建Log实例)。

 2.1 入门案例

引入依赖

<dependency>
	<groupId>commons-logging</groupId>
	<artifactId>commons-logging</artifactId>
	<version>1.2</version>
</dependency>
public void test() throws Exception {
	// 创建日志对象
	Log log = LogFactory.getLog(JULTest.class);
	// 日志记录输出
	log.fatal("fatal");
	log.error("error");
	log.warn("warn");
	log.info("info");
	log.debug("debug");
}

默认使用JDK14 默认的日志输出。

当加入log4j的依赖后,配置文件就使用的是log4j输出

2.2 为什么使用日志门面
  1. 面向接口开发,不再依赖具体的实现类。减少代码的耦合
  2. 项目通过导入不同的日志实现类,可以灵活的切换日志框架
  3. 统一API,方便开发者学习和使用
  4. 统一配置便于项目日志的管理
2.3 jcl 经典

经典白学已经被淘汰了

3. 日志门面

当我们的系统变的更加复杂的时候,我们的日志就容易发生混乱。随着系统开发的进行,可能会更新不 同的日志框架,造成当前系统中存在不同的日志依赖,让我们难以统一的管理和控制。就算我们强制要 求所有的模块使用相同的日志框架,系统中也难以避免使用其他类似spring,mybatis 等其他的第三方框 架,它们依赖于我们规定不同的日志框架,而且他们自身的日志系统就有着不一致性,依然会出来日志 体系的混乱。
所以我们需要借鉴 JDBC 的思想,为日志系统也提供一套门面,那么我们就可以面向这些接口规范来开 发,避免了直接依赖具体的日志框架。这样我们的系统在日志中,就存在了日志的门面和日志的实现。
常见的日志门面 : JCL、 slf4j
日志门面和日志实现的关系:

日志框架出现的历史顺序:

log4j -->JUL-->JCL--> slf4j --> logback --> log4j2

4. SLF4J的使用

简单日志门面 (Simple Logging Facade For Java) SLF4J 主要是为了给 Java 日志访问提供一套标准、规范 的API 框架,其主要意义在于提供接口,具体的实现可以交由其他日志框架,例如 log4j logback 等。 当然slf4j 自己也提供了功能较为简单的实现,但是一般很少用到。对于一般的 Java 项目而言,日志框架 会选择 slf4j-api  作为门面,配上具体的实现框架( log4j logback 等),中间使用桥接器完成桥接。
官方网站: https://www.slf4j.org/
SLF4J 是目前市面上最流行的日志门面。现在的项目中,基本上都是使用 SLF4J 作为我们的日志系统。
SLF4J 日志门面主要提供两大功能:
1. 日志框架的绑定
2. 日志框架的桥接
4.1 SLF4J入门
1. 添加依赖
<!--slf4j core 使用slf4j必須添加-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.27</version>
</dependency>
<!--slf4j 自带的简单日志实现 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.27</version>
</dependency>

2. demo

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Slf4jTest {
    public static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);
    @Test
    public void test1(){
        // 日志输出
        LOGGER.error("error");
        LOGGER.warn("wring");
        LOGGER.info("info"); // 默认级别
        LOGGER.debug("debug");
        LOGGER.trace("trace");
        // 可以使用占位符
        String name = "宝竹";
        LOGGER.info("hello: {}",name);
        try {
            int i = 10 / 0;
        }catch (Exception e){
//            e.printStackTrace();
            LOGGER.info("出现异常了 啊 a a");
        }

    }
}

4.2 绑定日志的实现(Binding
如前所述, SLF4J 支持各种日志框架。 SLF4J 发行版附带了几个称为 “SLF4J 绑定 jar 文件,每个绑定对应 一个受支持的框架。
使用 slf4j 的日志绑定流程 :
1. 添加 slf4j-api 的依赖
2. 使用 slf4j API 在项目中进行统一的日志记录
3. 绑定具体的日志实现框架
        1. 绑定已经实现了 slf4j 的日志框架 , 直接添加对应依赖
        2. 绑定没有实现 slf4j 的日志框架 , 先添加日志的适配器 , 再添加实现类的依赖
4. slf4j 有且仅有一个日志实现框架的绑定(如果出现多个默认使用第一个依赖日志实现)

 通过maven引入常见的日志实现框架: (日志实现需要要导入一个即可)

   <!--slf4j 日志门面 使用slf4j必须添加-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.36</version>
        </dependency>

        <!--slf4j 内置的简单实现-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.21</version>
        </dependency>

        <!--绑定logback 日志实现-->
      <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

        <!--sl4f-nop slf4j日志开关 导入后将不会开启日志输出-->
       <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-nop</artifactId>
            <version>1.7.25</version>
        </dependency>
	
 		<!--绑定log4j-->
        <!--绑定log4j的日志实现需要导入适配器 slf4j-log4j12-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.29</version>
        </dependency>
        <!--log4j日志实现-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

		<!--绑定jul-->
        <!--绑定jul 日志实现 需要导入适配器 slf4j-jdk14-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-jdk14</artifactId>
            <version>1.5.6</version>
        </dependency>

要切换日志框架,只需替换类路径上的slf4j绑定。例如,要从java.util.logging切换到log4j,只需将slf4j-jdk14-1.7.27.jar替换为slf4j-log4j12-1.7.27.jar即可。


SLF4J不依赖于任何特殊的类装载。实际上,每个SLF4J绑定在编译时都是硬连线的, 以使用一个且只有一个特定的日志记录框架。例如,slf4j-log4j12-1.7.27.jar绑定在编译时绑定以使用log4j。在您的代码中,除了slf4j-api-1.7.27.jar之外,您只需将您选择的一个且只有一个绑定放到相应的类路径位置。不要在类路径上放置多个绑定。以下是一般概念的图解说明。

 

2.3 桥接旧的日志框架(Bridging
通常,您依赖的某些组件依赖于 SLF4J 以外的日志记录 API 。您也可以假设这些组件在不久的将来不会切
换到 SLF4J 。为了解决这种情况, SLF4J 附带了几个桥接模块,这些模块将对 log4j JCL 和java.util.logging API的调用重定向,就好像它们是对 SLF4J API 一样。
桥接解决的是项目中日志的遗留问题,当系统中存在之前的日志 API ,可以通过桥接转换到 slf4j 的实现
1. 先去除之前老的日志框架的依赖
2. 添加 SLF4J 提供的桥接组件
3. 为项目添加 SLF4J 的具体实现

 假设我们原本项目用的 log4j

   <!--log4j日志实现-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
import org.apache.log4j.Logger;
import org.junit.Test;

public class Log4jTest1 {
    public static Logger LOGGER = Logger.getLogger(Log4jTest1.class);
    @Test
    public void test1(){ // 测试桥接
        LOGGER.info("hello log4j");
    }
}

当我们想换掉 log4j 使用日志门面绑定logback 日志实现,那么上述代码就会报错。

当使用桥接器后 ,上面代码就不会报错了

<!-- 1,先去除之前老的日志框架的依赖-->
<!--  <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
  </dependency>
  -->
<!-- 2. 添加SLF4J提供的桥接组件-->
		<dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
            <version>1.7.25</version>
        </dependency>
<!-- 3. 为项目添加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>

迁移的方式:
如果我们要使用SLF4J的桥接器,替换原有的日志框架,那么我们需要做的第一件事情,就是删除掉原有项目中的日志框架的依赖。然后替换成SLF4J提供的桥接器。
SLF4J提供的桥接器:

<!-- log4j桥接器-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.27</version>
</dependency>

<!-- jul 桥接器-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.27</version>
</dependency>

<!--jcl 桥接器-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.27</version>
</dependency>

注意

  • jcl-over-slf4j.jar和 slf4j-jcl.jar不能同时部署。前一个jar文件将导致JCL将日志系统的选择委托给 SLF4J,后一个jar文件将导致SLF4J将日志系统的选择委托给JCL,从而导致无限循环。
  • log4j-over-slf4j.jar和slf4j-log4j12.jar不能同时出现 (桥接器和适配器)
  • jul-to-slf4j.jar和slf4j-jdk14.jar不能同时出现
  • 所有的桥接都只对Logger日志记录器对象有效,如果程序中调用了内部的配置类或者是Appender,Filter等对象,将无法产生效果

当日志门面slf4j  桥接器  适配器  log4j  依赖同时存在,运行会报栈溢出错误

开始的时候使用log4j 具体实现输出,后面改造为 日志门面加上 其他日志输出模式。就需要加上 桥接器。log4j的功能被取消。然后日志门面调用适配器,适配器又调用桥接器。然后就死循环了。

SLF4J 原理解析
1. SLF4J 通过 LoggerFactory 加载日志具体的实现对象。
2. LoggerFactory 在初始化的过程中,会通过 performInitialization() 方法绑定具体的日志实现。
3. 在绑定具体实现的时候,通过类加载器,加载 org/slf4j/impl/StaticLoggerBinder.class
4. 所以,只要是一个日志实现框架,在 org.slf4j.impl 包中提供一个自己的 StaticLoggerBinder 类,在其中提供具体日志实现的LoggerFactory 就可以被 SLF4J 所加载

5. Logback

5.1 入门


Logback是由log4j创始人设计的另一个开源日志组件,性能比log4j要好。官方网站:https://logback.qos.ch/index.html

Logback主要分为三个模块:
logback-core:其它两个模块的基础模块
logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API
logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能

入门案例: 导入依赖

     <!--slf4j 日志门面-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.26</version>
        </dependency>
        <!--logback 日志实现 引入了 classic也引入了logback-core 他们有依赖关系-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogBackTest {
    public static final Logger LOGGER = LoggerFactory.getLogger(LogBackTest.class);
    @Test
    public void test1(){
        // 日志输出
        LOGGER.error("error");
        LOGGER.warn("wring");
        LOGGER.info("info");
        LOGGER.debug("debug");// 默认级别
        LOGGER.trace("trace");
    }
5.2 logback配置
logback 会依次读取以下类型配置文件:
logback.groovy
logback-test.xml
logback.xml 如果均不存在会采用默认配置
1. logback 组件之间的关系
        1. Logger:日志的记录器,把它关联到应用的对应的 context 上后,主要用于存放日志对象,也
        可以定义日志类型、级别。
        2. Appender:用于指定日志输出的目的地,目的地可以是控制台、文件、数据库等等。
        3. Layout:负责把事件转换成字符串,格式化的日志信息的输出。在 logback Layout 对象被封
        装在encoder 中。
2. 基本配置信息
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!--日志输出格式:
    %-5level
    %d{yyyy-MM-dd HH:mm:ss.SSS}日期
    %c类的完整名称
    %M为method
    %L为行号
    %thread线程名称
    %m或者%msg为信息
    %n换行
    格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度
    %m或者%msg为日志消息,%n是换行符
    -->
<!--    name 自定义名称-->
    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"></property>

    <!--定义日志文件保存路径属性-->
    <property name="my_dir" value="C:/Users/15121262356/Desktop/logs"></property>

    <!--控制台日志输出的 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>

    <!--日志文件输出的 appender : 输出到指定文件 -->
    <appender name="my_file" class="ch.qos.logback.core.FileAppender">
        <!--日志文件保存路径-->
        <file>${my_dir}/logback.log</file>
        <!--日志消息格式配置-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <!--html 格式日志文件输出 appender  :输出到指定文件 -->
    <appender name="htmlFile" class="ch.qos.logback.core.FileAppender">
        <!--日志文件保存路径-->
        <file>${my_dir}/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}%c%M%L%thread%m</pattern>
            </layout>
        </encoder>
    </appender>

    <!--root logger 配置 : 输出级别  和 输出到哪里-->
    <root level="ALL">
        <appender-ref ref="console"/>
        <appender-ref ref="my_file"/>
        <appender-ref ref="htmlFile"/>
    </root>
</configuration>

配置进阶

过滤器: 配置到appender当中,error 通过,其余的拦截。

异步日志: 提高性能,后续输出到日志文件当中。

自定义日志:  com.csi包 下面的日志遵循自己的日志规则,additivity="false" 表示不继承父类的输出规则,只在控制台输出。不会在文件当中输出了。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

<!--    name 自定义名称-->
    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n"></property>

    <!--定义日志文件保存路径属性-->
    <property name="my_dir" value="C:/Users/15121262356/Desktop/logs"></property>

    <!--控制台日志输出的 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>
 

    <!--日志拆分和归档压缩的 appender 对象-->
    <appender name="my_rollFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!--日志文件保存路径-->
    <file>${my_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">
        <!--按照时间和压缩格式声明拆分的文件名  : gz 是压缩包格式-->
        <fileNamePattern>${my_dir}/rolling.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
        <!--按照文件大小拆分-->
        <maxFileSize>1MB</maxFileSize>
    </rollingPolicy>

        <!--日志级别过滤器-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!--日志过滤规则  :error通过,其他的拦截-->
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--异步日志 : 提高性能后续再生成日志-->
    <appender name="my_async" class="ch.qos.logback.classic.AsyncAppender">
        <!--指定某个具体的 appender-->
        <appender-ref ref="my_rollFile"/>
    </appender>

    <!--自定义 looger 对象
          additivity="false" 自定义 logger 对象是否继承 rootLogger 
       -->
    <logger name="com.csi" level="info" additivity="false">
        <appender-ref ref="console"/>
    </logger>

    <!--root logger 配置 : 输出级别  和 输出到哪里-->
    <root level="ALL">
        <appender-ref ref="console"/>
<!--        <appender-ref ref="my_file"/>-->
        <appender-ref ref="my_async"/>
    </root>
</configuration>
官方提供的 log4j.properties 转换成 logback.xml
https://logback.qos.ch/translator/
logback官方配置: https://logback.qos.ch/access.html#configuration

6. log4j2的使用

Apache Log4j 2 是对 Log4j 的升级版,参考了 logback 的一些优秀的设计,并且修复了一些问题,因此带
来了一些重大的提升,主要有:
  • 异常处理,在logback中,Appender中的异常不会被应用感知到,但是在log4j2中,提供了一些异常处理机制。
  • 性能提升, log4j2相较于log4j logback都具有很明显的性能提升,后面会有官方测试的数据。
  • 自动重载配置,参考了logback的设计,当然会提供自动刷新参数配置,最实用的就是我们在生产上可以动态的修改日志的级别而不需要重启应用。
  • 无垃圾机制,log4j2在大部分情况下,都可以使用其设计的一套无垃圾机制,避免频繁的日志收集导致的jvm gc
官网: https://logging.apache.org/log4j/2.x/
6.1 Log4j2入门
目前市面上最主流的日志门面就是 SLF4J ,虽然 Log4j2 也是日志门面,因为它的日志实现功能非常强 大,性能优越。所以大家一般还是将Log4j2 看作是日志的实现, Slf4j + Log4j2 应该是未来的大势所趋。
1. 添加依赖
<!-- Log4j2 门面API-->
<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>
2. JAVA 代码
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

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");
    }
}
3. 使用 slf4j 作为日志的门面 , 使用 log4j2 作为日志的实现
<!-- Log4j2 门面API-->
<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>
<!--使用slf4j作为日志的门面,使用log4j2来记录日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!--为slf4j绑定日志实现 log4j2的适配器 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.10.0</version>
</dependency>
6.2 Log4j2配置 
log4j2 默认加载 classpath(类路径下) 下的 log4j2.xml 文件中的配置。
<?xml version="1.0" encoding="UTF-8"?>
<!-- status="warn" 日志框架本身的输出日志级别
    monitorInterval="5" 自动加载配置文件的间隔时间,不低于 5 秒 -->
<Configuration status="debug" monitorInterval="5">
    <!--集中配置属性进行管理   使用时通过:${name} -->
    <properties>
        <property name="LOG_HOME">C:/Users/15121262356/Desktop/logs</property>
    </properties>
    <!--日志处理-->
    <Appenders>
        <!--控制台输出 appender-->
        <Console name="Console" target="SYSTEM_ERR">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n" />
        </Console>
        <!--日志文件输出 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>
        <!--<Async name="Async">-->
        <!--<AppenderRef ref="file"/>-->
        <!--</Async>-->
        <!--使用随机读写流的日志文件输出 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>
        <!--按照一定规则拆分的日志文件的 appender-->
        <RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log"
                     filePattern="/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyy-MM-dd-HH-mm}-%i.log">
            <!--日志级别过滤器-->
            <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>
    <!--logger 定义-->
    <Loggers>
        <!--自定义异步 logger 对象 includeLocation="false" 关闭日志记录的行号信息
            additivity="false" 不在继承 rootlogger 对象 -->
        <AsyncLogger name="com.itheima" level="trace" includeLocation="false" additivity="false">
            <AppenderRef ref="Console"/>
        </AsyncLogger>
        <!--使用 rootLogger 配置 日志级别 level="trace"-->
        <Root level="trace">
            <!--指定日志使用的处理器-->
            <AppenderRef ref="Console" />
            <!--使用异步 appender-->
<!--            <AppenderRef ref="Async" />-->
        </Root>
    </Loggers>
</Configuration>
6.3 Log4j2异步日志
异步日志
log4j2 最大的特点就是异步日志,其性能的提升主要也是从异步日志中受益,我们来看看如何使用
log4j2 的异步日志。
  • 同步日志 (主线程日志输出需要执行完这么多步骤才可以往下执行,会严重影响性能)

  • 异步日志 (主线程创建一个Logger对象后,生成logEvent后 就交给阻塞队列了,主线程就不管了可以执行其他操作了。log4j2 开启新的线程来执行接下来的操作不会影响主线程)

Log4j2 提供了两种实现日志的方式,一个是通过 AsyncAppender ,一个是通过 AsyncLogger ,分别对应 前面我们说的Appender 组件和 Logger 组件。
注意:配置异步日志需要添加依赖

 

<!--异步日志依赖-->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.4</version>
</dependency>
1. AsyncAppender方式   (不推荐性能和lockback差不多)
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">

	<properties>
		<property name="LOG_HOME">D:/logs</property>
	</properties>
	
	<Appenders>
		<File name="file" fileName="${LOG_HOME}/myfile.log">
			<PatternLayout>
				<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
			</PatternLayout>
		</File>
		异步日志 导入依赖的方式
		<Async name="Async">
			<AppenderRef ref="file"/>
		</Async>
		
	</Appenders>
	
	<Loggers>
		<Root level="error">
			<AppenderRef ref="Async"/>
		</Root>
	</Loggers>
</Configuration>
2. AsyncLogger方式   推荐
AsyncLogger 才是 log4j2 的重头戏,也是官方推荐的异步方式。它可以使得调用 Logger.log 返回的
更快。你可以有两种选择:全局异步和混合异步。
全局异步: 就是,所有的日志都异步的记录,在配置文件上不用做任何改动,只需要添加一个
log4j2.component.properties   配置;
 
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

混合异步:就是,你可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活。

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
	<properties>
		<property name="LOG_HOME">D:/logs</property>
	</properties>
	<Appenders>
		<File name="file" fileName="${LOG_HOME}/myfile.log">
			<PatternLayout>
				<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
			</PatternLayout>
		</File>
		<Async name="Async">
			<AppenderRef ref="file"/>
		</Async>
	</Appenders>
	<Loggers>
		<!--com.chen 日志是异步的,root日志是同步的。-->
  <!--自定义异步 logger 对象 includeLocation="false" 
                    关闭日志记录的行号信息(因为行号信息可能影响异步日志性能)
            additivity="false" 不在继承 rootlogger 对象 -->
		<AsyncLogger name="com.csi" level="trace"
		includeLocation="false" additivity="false">
			<AppenderRef ref="file"/>
		</AsyncLogger>
		
		<Root level="info" includeLocation="true">
			<AppenderRef ref="file"/>
		</Root>
	</Loggers>
</Configuration>

如上配置: com.itheima 日志是异步的, root 日志是同步的。
使用异步日志需要 注意的问题
1. 如果使用异步日志, AsyncAppender AsyncLogger 和全局日志,不要同时出现。性能会和
AsyncAppender 一致,降至最低。
2. 设置 includeLocation=false ,打印位置(行号信息)信息会急剧降低异步日志的性能,比同步日志还要慢。

6.4 Log4j2的性能
Log4j2 最牛的地方在于异步输出日志时的性能表现, Log4j2 在多线程的环境下吞吐量与 Log4j
Logback 的比较如下图。下图比较中 Log4j2 有三种模式:
1 )全局使用异步模式;
2 )部分 Logger 采用异步模式;
3 )异步 Appender 。可以看出在前两种模式下, Log4j2 的性能较之 Log4j Logback 有很大的
优势。

 

无垃圾记录
垃圾收集暂停是延迟峰值的常见原因,并且对于许多系统而言,花费大量精力来控制这些暂停。
许多日志库(包括以前版本的 Log4j )在稳态日志记录期间分配临时对象,如日志事件对象,字符串,字符数组,字节数组等。这会对垃圾收集器造成压力并增加GC 暂停发生的频率。
从版本 2.6 开始,默认情况下 Log4j 无垃圾 模式运行,其中重用对象和缓冲区,并且尽可能不分配临时对象。还有一个“ 低垃圾 模式,它不是完全无垃圾,但不使用 ThreadLocal 字段。
Log4j 2.6 中的无垃圾日志记录部分通过重用 ThreadLocal 字段中的对象来实现,部分通过在将文本转换为字节时重用缓冲区来实现。
使用 Log4j 2.5 :内存分配速率 809 MB / 秒, 141 个无效集合。

 

Log4j 2.6没有分配临时对象:0(零)垃圾回收。 

 

 

有两个单独的系统属性可用于手动控制 Log4j 用于避免创建临时对象的机制:
  • log4j2.enableThreadlocals - 如果“true”(非Web应用程序的默认值)对象存储在ThreadLocal字段中并重新使用,否则将为每个日志事件创建新对象。
  • log4j2.enableDirectEncoders - 如果将“true”(默认)日志事件转换为文本,则将此文本转换 为字节而不创建临时对象。注意: 由于共享缓冲区上的同步,在此模式下多线程应用程序的同步日志记录性能可能更差。如果您的应用程序是多线程的并且日志记录性能很重要,请考虑使用异步记录器。

7. SpringBoot中的日志使用

springboot 框架在企业中的使用越来越普遍, springboot 日志也是开发中常用的日志系统。 springboot 默认就是使用SLF4J 作为日志门面, logback 作为日志实现来记录日志。  

7.1 SpringBoot中的日志设计
springboot 中的日志
<dependency>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</dependency>
依赖关系图:
总结:
1. springboot 底层默认使用 logback 作为日志实现。
2. 使用了 SLF4J 作为日志门面
3. JUL 也转换成 slf4j
4. 也可以使用 log4j2 作为日志门面,但是最终也是通过 slf4j 调用 logback

 

7.2 SpringBoot日志使用
1. springboot 中测试打印日志
@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");
}
}
2. 修改默认日志配置 application.properties 中
# 指定自定义 logger 对象日志级别
logging.level.com.csi=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

# 指定项目使用的具体环境
spring.profiles.active=dev
3. 指定配置
给类路径下放上每个日志框架自己的配置文件; SpringBoot 就不使用默认配置的了

 logback.xml:直接就被日志框架识别了

4. 使用 SpringBoot 解析日志配置
        logback-spring.xml:由 SpringBoot 解析日志配置  (可配置生产和测试的log输出)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <property name="pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %c %M %L [%thread] -------- %m %n"></property>

    <!--控制台日志输出的 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>

    <!--自定义 looger 对象
        additivity="false" 自定义 logger 对象是否继承 rootLogger-->
    <logger name="com.csi" level="info" additivity="false">
        <appender-ref ref="console"/>
    </logger>
</configuration>
application.properties
spring.profiles.active=dev
5. 将日志切换为 log4j2
 先将默认启动器排除,然后条件新的启动器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>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值