关于java:还不了解日志框架吗🤪

2021/12/14

  • 间隔过年还有 44天想家😘这两天有一件事闹的 满城风雨的~
  • 某个日志框架,呈现了重大的bug ,据说好多,大厂大佬加班加点的改bug~😱

    而后,自己想如同自己对日志框架还不怎么了理解,只晓得导入依赖,配置文件,失常的应用…更深刻的就不太革除了...

    连忙,学习总结了一波笔记~👍

日志框架📕

日志的概念

日志文件是用于记录零碎操作事件的文件汇合

  • 在计算机领域日志文件logfile

    是一个记录了产生在运行中的操作系统或其他软件中的事件的文件,或者记录了在网络聊天软件的用户之间发送的音讯。

为什么须要日志:

  • 对于一个应用程序来说日志记录是必不可少的一部分

    能够帮忙,开发者疾速的定位问题,找到问题,进行解决…👍

现有的日志框架🛠

呈现日期排序:

Java 日志框架倒退了很多年,曾经呈现了很多个版本~倒退过程也比拟凌乱~

  • log4j → JUL → JUCCommons Logging → Slf4j → Logback → log4j2

Java日志框架发展史✍:

log4j 1999

  • 它是 Apache 软件基金会的一个我的项目,在 jdk1.3 之前,还没有现成的日志框架:

    Java 工程师只能应用原始的 System.out.println() System.err.println() 或者 e.printStackTrace()

    把 debug 日志写到 StdOut 流,谬误日志写到 ErrOut 流,以此记录应用程序的运行状态

  • 这种原始的日志记录形式缺点显著,不仅无奈实现定制化,而且日志的输入粒度不够细.
  • 1999 年,大牛 Ceki Gülcü 切基·居尔库 创立了 Log4j 我的项目

JUL 2002

  • Log4j 作为 Apache 基金会的一员,Apache 心愿将 Log4j 引入 jdk 不过被 sun 公司回绝了
  • 随后,sun 模拟 Log4j,在 jdk1.4 中引入了 JUL 🙃

    sun公司真傲娇,终于晓得怎么破产的了~

JCL 2002

  • 因为,过后时面上呈现了两种日志框架 不同的应用形式,不同日志级别~ 对开发者不是很敌对~
  • 于是,2002年 阿帕奇 推出了 JCL日志门面 定义一套接口,具体实现由Log4j 或 JUL 来实现

    程序运行时会应用 ClassLoader类加载器 寻找和载入底层的日志库,因而能够自由选择由 log4j 或 JUL 来实现日志性能

  • 相似于JDBC

    定义了一组数据库的 增删改查接口, Mysql Oracle...数据库都实现类这些接口能够做到,无需更换代码切换数据库~

Slf4j 2005 & Logback 2006

  • 两个起因,JCL的实现形式太过麻烦,前期不不便扩大新的日志框架

    Ceki Gülcü 大佬,前面来到了阿帕奇,单独开发了一个新的 日志门面 Slf4j Logback是其的实现~

  • 它相较于 log4j 有更快的执行速度和更欠缺的性能大佬牛逼呀!🐂

log4j2 2014

  • 为了保护在 Java 日志江湖的位置

    避免 JCL、Log4j 被 Slf4j、Logback 组合取代 ,2014 年 Apache 推出了 Log4j2 来自老东家的'制裁🙃'

  • Log4j 2 与 log4j 不兼容不存在管来,通过大量深度优化,其性能显著晋升👍

日志门面 与 日志框架:

通过下面,咱们曾经晓得罕用的日志框架有:Log4j JUL JCL Slf4j Logback Log4j2

这些日志框架能够分为两种类型:门面日志日志零碎

日志门面:设计模式 外观模式

JCL、slf4j

  • 只提供日志相干的接口定义,即相应的 API 提供简略实现
  • 为了,不便不同的日志, 实现, 不会对代码进行大改变~进步开发者的应用~

日志零碎:

JUL、logback、log4j、log4j2

  • 与日志门面绝对,它提供了具体的日志接口实现,应用程序通过它执行日志打印的性能

JUL

JUL全称Java.util.Logging

  • 是java原生的日志框架,应用时不须要另外援用第三方类库
  • 绝对其余日志框 架使用方便,学习简略,可能在小型利用中灵便应用

JUL 架构介绍

Logger

  • 记录器,应用程序通过 getLogger(); 获取 Logger 对象,调用其 API 来公布日志信息
  • Logger 通常被认为是拜访日志零碎的入口程序

Handler

  • 处理器,每个 Logger 都会关联一个或者是一组 Handler,Console File
  • Logger 会将日志交给关联的 Handler 去做解决,由 Handler 负责将日志做记录.
  • Filter

    过滤器,依据须要定制哪些信息会被记录,哪些信息会被略过

  • Formatter

    格式化组件,它负责对日志中的数据和信息进行转换和格式化,所以它决定了咱们输入日志最终的模式

  • Level

    日志的输入级别,每条日志音讯都有一个关联的级别。依据输入级别的设置,用来展示最终所出现的日志信息

日志记录器logger 有本人默认的,Filter Formatter Level,能够与一个 或 多个Hanlder关联进行日志输入~

入门Demo:

创立一个JUL Maven工程

JUL 是Java 自身提供的,所以,不须要引入任何依赖…

JULTest.Java

import org.junit.Test;
import java.util.logging.Level;
import java.util.logging.Logger;

public class JULTest {
    //入门案例:
    @Test
    public void Test(){
        //1.获取日志记录器对象 Logger.getLogger("参数是一个惟一的字符串,一般来说应用类的'全路径名'");
        Logger logger = Logger.getLogger("com.wsm.JULTest");
        //2.日志记录输入
        logger.info("输入日志,info 级别~");

    /** 罕用办法 */
        //通过 .log(); 办法,指定日志的输入级别
        logger.log(Level.INFO,"指定输入级别: info~");
        logger.log(Level.WARNING,"指定输入级别: WARNING~");

        //通过占位符模式,输入日志, 相似于 printf();
        String name = "WSM";
        Integer age = 3;
        logger.log(Level.INFO,"我叫: “{0},往年{1}岁",new Object[]{name,age});
    }
}

控制台输入:

十二月 15, 2021 9:43:06 下午 com.wsm.JULTest Test
信息: 输入日志,info 级别~
十二月 15, 2021 9:43:06 下午 com.wsm.JULTest Test
信息: 指定输入级别: info~
十二月 15, 2021 9:43:06 下午 com.wsm.JULTest Test
正告: 指定输入级别: WARNING~
十二月 15, 2021 9:43:06 下午 com.wsm.JULTest Test
信息: 我叫: “WSM,往年3岁

JUL 日志级别阐明及展现

JUL 一个有七个日志级别,两个非凡的 off All

日志级别 数值 阐明
OFF Integer.MAX_VALUE 最大整数 敞开所有音讯的日志记录
SEVERE中: 色歪啊~ 1000 错误信息最高级的日志级别
WARNING 900 正告信息
INFO 800 默认信息默认级别
CONFIG 700 配置信息
FINE 500 详细信息(少)
FINER 400 详细信息(中)
FINEST 300 详细信息(多) 最低级的日志级别
ALL Integer.MIN_VALUE最小整数 启用所有音讯的日志记录

数值的意义在于: 设置指定了日志级别,最终展现的信息,必须大于> 指定级别的数值!

  • OFF 和 ALL 是非凡的日志级别 全副敞开 全副启动

高级Demo

验证,JUL默认输入级别:

//查看JUL默认输入级别
@Test
public void Test2(){
    // 1.获取日志记录器对象
    Logger logger = Logger.getLogger("com.itheima.JULTest");    //参数,保障惟一即可,个别填写 类路径名;
    // 2.日志记录输入
    logger.severe("severe");
    logger.warning("warning");
    logger.info("info");                                         //默认日志输入级别
    logger.config("config");
    logger.fine("fine");
    logger.finer("finer");
    logger.finest("finest");
}

//自定义日志级别1.0
@Test
public void Test3(){
    // 1.获取日志记录器对象
    Logger logger = Logger.getLogger("com.itheima.JULTest");    //参数,保障惟一即可,个别填写 类路径名;

    // 2.设置日志输入级别: 扭转Logger 日志对象
    // 首先关闭系统默认配置
    logger.setUseParentHandlers(false);
    // 从新,配置日志具体级别 (你认为这样就行了吗? 执行发现啥也没有~ 因为,当初日志配置还不残缺,短少了 Handler 还有 Formatter )
    logger.setLevel(Level.ALL);

    // 3.日志记录输入
    logger.severe("severe");
    logger.warning("warning");
    logger.info("info"); 
    logger.config("config");
    logger.fine("fine");
    logger.finer("finer");
    logger.finest("finest");
}

//自定义日志级别2.0
@Test
public void Test4(){
    // 1.获取日志记录器对象
    Logger logger = Logger.getLogger("com.itheima.JULTest");
    // 2.设置日志输入级别: 扭转Logger 日志对象
    // 首先关闭系统默认配置
    logger.setUseParentHandlers(false);
    // 从新,配置日志具体级别
    logger.setLevel(Level.ALL);
    // 创立ConsolHhandler 控制台输入
    ConsoleHandler consoleHandler = new ConsoleHandler();
    // 创立简略格局转换对象
    SimpleFormatter simpleFormatter = new SimpleFormatter();
    // 将Handler 和 Formatter进行关联: logger ——关联—— Handler ——关联—— Formatter
    consoleHandler.setFormatter(simpleFormatter);
    logger.addHandler(consoleHandler);
    // 设consoleHandler 控制台输入的级别~
    consoleHandler.setLevel(Level.ALL);

    // 3.日志记录输入
    logger.severe("severe");
    logger.warning("warning");
    logger.info("info"); 
    logger.config("config");
    logger.fine("fine");
    logger.finer("finer");
    logger.finest("finest");
}
Test2
十二月 15, 2021 9:45:17 下午 com.itheima.JULTest testLogLevel
重大: severe
十二月 15, 2021 9:45:17 下午 com.itheima.JULTest testLogLevel
正告: warning
十二月 15, 2021 9:45:17 下午 com.itheima.JULTest testLogLevel
信息: info

Test3
CMD输入空!因为,日志的配置不残缺,短少了 Handler Formatter

Test4
十二月 15, 2021 10:33:34 下午 com.wsm.JULTest Test4
重大: severe
十二月 15, 2021 10:33:34 下午 com.wsm.JULTest Test4
正告: warning
十二月 15, 2021 10:33:34 下午 com.wsm.JULTest Test4
信息: info
十二月 15, 2021 10:33:34 下午 com.wsm.JULTest Test4
配置: config
十二月 15, 2021 10:33:34 下午 com.wsm.JULTest Test4
具体: fine
十二月 15, 2021 10:33:34 下午 com.wsm.JULTest Test4
较具体: finer
十二月 15, 2021 10:33:34 下午 com.wsm.JULTest Test4
十分具体: finest

Test2

  • 发现输入的日志之后三行… 其它的都没有输入进去… 因为,JUL的默认级别就是 info 小于info的级别数值都输入不进去

Test3

  • 没有一行数据,因为:日志的配置不残缺,短少了 Handler Formatter

Test4

  • 终于,残缺的输入了所有 级别的日志
  • 创立了一个控制台的 ConsoleHandler 简略的输入格局 SimpleFormatter 并进行关联~🔗 最重要的是,设置了Console的输入级别,这个是你控制台输入的日志级别!
  • 留神:

    consoleHandler.setLevel(Level.ALL); 设置的级别 ,取绝于 logger.setLevel(Level.ALL);

    logger 是真正设置输出的日志级别,但它因为配置不残缺须要,consoleHandler输入. consoleHandler的级别在大 logger级别放的小,输入的数据也不多~

    两者关系就像是两个水桶💧 logger小桶 放在consoleHandler大桶

    大桶的出水口在大,小桶一次出一滴水,大桶也只有一滴水流速🌊

    小桶出水口很大,每次流很多水,大桶的口小,每次流出的水也不大!

设置,日志文件输入:

//日志文件的输入:
@Test
public void Test5() throws IOException {
    // 1.获取日志记录器对象
    Logger logger = Logger.getLogger("com.itheima.JULTest");
    // 2.设置日志输入级别: 扭转Logger 日志对象
    // 首先关闭系统默认配置
    logger.setUseParentHandlers(false);
    // 从新,配置日志具体级别
    logger.setLevel(Level.ALL);         //设置小桶流速!
    // 创立格局
    SimpleFormatter simpleFormatter = new SimpleFormatter();
    // 创立ConsolHhandler 控制台输入: 关联 输入级别
    ConsoleHandler consoleHandler = new ConsoleHandler();
    consoleHandler.setFormatter(simpleFormatter);
    logger.addHandler(consoleHandler);
    consoleHandler.setLevel(Level.ALL);

    // 创立文件输入 Handler
    FileHandler fileHandler = new FileHandler("C:\\Users\\王斯明\\Desktop\\jul.log");  /** 要确保输入的目录存在! */
    fileHandler.setFormatter(simpleFormatter);
    logger.addHandler(fileHandler);
    // 不设置,日志级别,输入日志~, 发现默认输入了全副, (继承了小桶logger的流量~)

    logger.severe("severe");
    logger.warning("warning");
    logger.info("info");
    logger.config("config");
    logger.fine("fine");
    logger.finer("finer");
    logger.finest("finest");
}

  • 留神⚠:

    Logger 能够持有多个处理器 Handler

    FileHandler 来设置,日志的输入文件,地址目录要当时存在!

    如果不指定,输入日志级别,则默认,依据logger设置的级别Logger对象的父子关系~

JUL Logger父子关系:

// Logger对象父子关系
@Test
public void Test6(){
    Logger logger1 = Logger.getLogger("com.wsm");
    Logger logger2 = Logger.getLogger("com");

    // 测试
    System.out.println("logger1下级 与 logger2比拟:"+(logger1.getParent() == logger2));
    // 所有日志记录器的顶级父元素 LogManager$RootLogger,name ""
    System.out.println("logger2 Parent:"+logger2.getParent() + ",name:" + logger2.getParent().getName());

    System.out.println("上级会默认继承下级的配置,扭转logger2 logger1也会产生扭转~");
    // 敞开默认配置
    logger2.setUseParentHandlers(false);
    // 设置logger2日志级别
    // 自定义配置日志级别
    // 创立ConsolHhandler 控制台输入
    ConsoleHandler consoleHandler = new ConsoleHandler();
    // 创立简略格局转换对象
    SimpleFormatter simpleFormatter = new SimpleFormatter();
    // 进行关联
    consoleHandler.setFormatter(simpleFormatter);
    logger2.addHandler(consoleHandler);
    // 配置日志具体级别
    logger2.setLevel(Level.ALL);
    consoleHandler.setLevel(Level.ALL);
    
    System.out.println();
    System.out.println("输入logger1 发现,输入级别变成了 info");
    logger1.severe("severe");
    logger1.warning("warning");
    logger1.info("info");
    logger1.config("config");
    logger1.fine("fine");
    logger1.finer("finer");
    logger1.finest("finest");
}
logger1下级 与 logger2比拟:true
logger2 Parent:java.util.logging.LogManager$RootLogger@61e4705b,name:
上级会默认继承下级的配置,扭转logger2 logger1也会产生扭转~

输入logger1 发现,输入级别变成了 info
十二月 16, 2021 12:00:11 上午 com.wsm.JULTest Test6
重大: severe
十二月 16, 2021 12:00:11 上午 com.wsm.JULTest Test6
正告: warning
十二月 16, 2021 12:00:11 上午 com.wsm.JULTest Test6
信息: info
十二月 16, 2021 12:00:11 上午 com.wsm.JULTest Test6
配置: config
十二月 16, 2021 12:00:11 上午 com.wsm.JULTest Test6
具体: fine
十二月 16, 2021 12:00:11 上午 com.wsm.JULTest Test6
较具体: finer
十二月 16, 2021 12:00:11 上午 com.wsm.JULTest Test6
十分具体: finest
  • JUL 的logger 对象具备父子关系

    降落会默认具备,下级的属性配置…`默认的下级是 LogManager$RootLogger@61e4705b,name

JUL 配置文件模式应用:

下面的,硬编码模式,能够实现简略的 日志记录输入,然而理论开发中,并不不便 开发中应用🙁

  • 下面咱们晓得,logger对象对象具备 父子关系 默认的下级是, LogManager$RootLogger@61e4705b,name
  • 那这个 LogManager$RootLogger@61e4705b,name 又是哪里来的,又为什么是默认 info 配置🧐?

源码剖析

  • Logger.getLogger(""); 办法,获取一个日志记录器对象ctrl+右击 进入办法~
  • 发现只有一个 demandLogger(); 进入!,

    发现LogManager manager = LogManager.getLogManager(); 获取一个LogManager日志管理器对象!LogManager是一个单例对象,负责记录和治理日志对象

  • 进入 getLogManager(); 进入 ensureLogManagerInitialized(); 发现:owner.readPrimordialConfiguration();加载配置文件!

    加载配置文件之后,就会执行 owner.rootLogger = owner.new RootLogger(); 设置顶级的,==rootLogger 对象==

  • 进入readPrimordialConfiguration(); 办法查看,加载配置文件流程:发现readConfiguration(); 读取配置文件,持续进入👉

    首先:String cname = System.getProperty("java.util.logging.config.class"); 以后零碎是否存在一个配置类!没有则往下!

    判断 String fname = System.getProperty("java.util.logging.config.file"); 当然零碎下 有没有 自定义的配置文件!

    如果没有则:fname = System.getProperty("java.home");获取Java装置目录,

    File f = new File(fname, "lib"); f = new File(f, "logging.properties"); 读取,JDK目录中的lib目录下的 logging.properties👍

logging.properties

  • 自己移除了,官网的英文正文换成了,失常正文!
# 设置一个控制台输入的 Handler
handlers= java.util.logging.ConsoleHandler
# 设置默认的日志级别 Info
.level= INFO

# 配置默认的 文件处理器
# 指定日志文件默认的输入门路, (%h输入以后设施的用户目录) (%u输入日志的文件后缀1 2 3...)
java.util.logging.FileHandler.pattern = %h/java%u.log
# 以后文件最大存储多少条记录...
java.util.logging.FileHandler.limit = 50000
# 以后文件的数量,输入日志文件的个数~
java.util.logging.FileHandler.count = 1
# 以后 处理器输入文件的格局, 默认 xml格局输入!
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

# 控制台处理器的 默认级别INfo
java.util.logging.ConsoleHandler.level = INFO
# 日志输入的格局
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

# Example to customize the SimpleFormatter output format 
com.xyz.foo.level = SEVERE

因而,咱们只须要,负责这个配置文件保障名字不变,到我的项目的 resources资源目录下即可通过配置文件模式,配置日志信息!

log配置文件模式进行操作:

resources资源目录下,增加 logging.properties

# RootLogger 顶级父元素指定的默认处理器为:ConsoleHandler,FileHandler (设置RootLogger 最高价关联的处理器)
handlers= java.util.logging.ConsoleHandler,java.util.logging.FileHandler
# RootLogger 顶级父元素默认的日志级别为:ALL
.level= ALL
# 敞开默认配置
com.itheima.useParentHanlders = false
        
# 向日志文件输入的 handler 对象
# 指定日志文件门路 /logs/java0.log  要确保装置目录存在!
java.util.logging.FileHandler.pattern = /java%u.log 
# 指定日志文件内容大小
java.util.logging.FileHandler.limit = 50000
# 指定日志文件数量
java.util.logging.FileHandler.count = 1
# 指定 handler 对象日志音讯格局对象
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
# 指定以追加形式增加日志内容,(如果不增加,每一次增加的数据,都会替换之前的数据)
java.util.logging.FileHandler.append = true

# 向控制台输入的 handler 对象
# 指定 handler 对象的日志级别
java.util.logging.ConsoleHandler.level = ALL
# 指定 handler 对象的日志音讯格局对象
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 指定 handler 对象的字符集
java.util.logging.ConsoleHandler.encoding = UTF-8
# 指定日志音讯格局
java.util.logging.SimpleFormatter.format = %4$s: %5$s [%1$tc]%n

JULConfigTest.Java

/** 配置文件模式应用,JUL */
public class JULConfigTest {
    @Test
    public void Test() throws Exception{
        // 读取配置文件,通过类加载器
        InputStream ins = JULTest.class.getClassLoader().getResourceAsStream("logging.properties");
        // 创立LogManager
        LogManager logManager = LogManager.getLogManager();
        // 通过LogManager加载配置文件
        logManager.readConfiguration(ins);
        // 创立日志记录器
        Logger logger = Logger.getLogger("com.wsm");
        logger.info("--------------------------------------------------------------------------------------------");
        logger.severe("severe");
        logger.warning("warning");
        logger.info("info");
        logger.config("config");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");

        System.out.println("");
//        // 创立日志记录器2
//        Logger logger2 = Logger.getLogger("www");
//        logger2.severe("severe");
//        logger2.warning("warning");
//        logger2.info("info");
//        logger2.config("config");
//        logger2.fine("fine");
//        logger2.finer("finer");
//        logger2.finest("finest");
    }
}

自定义配置:日志对象级别:

除了通过 logging.properties配置文件,设置公众的配置,也能够独自对于某一种的 日志对象进行设置!

## 自定义Logger应用,独自设置自定义的, 日志元素
www.handler = java.util.logging.ConsoleHandler
www.level = INFO

解除下面 Logger.getLogger("www"); 正文!

ok, JUL就理解这么多了,当初用的也很少了…简直没有公司在应用了...

Log4j

Log4j是Apache下的一款开源的日志框架:官方网站

  • 通过在Log4J,咱们能够管制日志信息输入到:控制台文件甚至是数据库中
  • 咱们能够管制每一条日志的输入格局,通过定义日志的输入级别,能够 更灵便的管制日志的输入过程

Log4j组件

Log4J 次要由:Loggers日志记录器 Appenders输入端 Layout日志格式化器

Loggers日志记录器

管制日志的输入级别与日志是否输入

  • Logger.getLogger(类的全限定名/类对象~);
  • Logger的名字大小写敏感,其命名有继承机制

    com.wsm 会继承,com限定名的日志属性~

  • Log4J中有一个非凡的logger叫做“root”

    他是所有logger的根,也就意味着其余所有的logger都会间接 或者间接地继承自rootroot logger能够用Logger.getRootLogger()办法获取

Appenders输入端

指定日志的输入中央输入到控制台、文件

输入端类型 作用
ConsoleAppender 将日志输入到控制台
FileAppender 将日志输入到文件中
DailyRollingFileAppender 将日志输入到一个日志文件,并且每天输入到一个新的文件
RollingFileAppender 将日志信息输入到一个日志文件,并且指定文件的尺寸,当文件大小达到指定尺寸时,会主动把文件改名,同时产生一个新的文件
JDBCAppender 把日志信息保留到数据库中

Layout日志格式化器

管制日志信息的输入格局

格式化器类型 作用
HTMLLayout 格式化日志输入为HTML表格模式
SimpleLayout 简略的日志输入格式化,打印的日志格局为(info – message 输出什么打印什么.
PatternLayout 最弱小的格式化期,能够依据自定义格局输入日志,如果没有指定转换格局, 就是用默认的转换格局

Layout的格局:

log4j 采纳相似 C 语言的 printf 函数的打印格局格式化日志信息,具体的占位符及其含意如下:
    %m 输入代码中指定的日志信息
    %p 输入优先级,及 DEBUG、INFO 等
    %n 换行符(Windows平台的换行符为 "\n",Unix 平台为 "\n")
    %r 输入自利用启动到输入该 log 信息消耗的毫秒数
    %c 输入打印语句所属的类的全名
    %t 输入产生该日志的线程全名
    %d 输入服务器以后工夫,默认为 ISO8601,也能够指定格局,        如:%d{yyyy年MM月dd日 HH:mm:ss}
    %F 输入日志音讯产生时所在的文件名称
    %L 输入代码中的行号
    %% 输入一个 "%" 字符
    
    %l 输入日志工夫产生的地位,包含类名%c、线程%t、及在代码中的行数%L        如:Test.main(Test.java:10)
    
能够在 % 与字符之间加上修饰符来管制最小宽度、最大宽度和文本的对其形式. 如:
    %5c     输入category名称,最小宽度是5,category<5,默认的状况下右对齐
    %-5c     输入category名称,最小宽度是5,category<5,"-"号指定左对齐,会有空格
    %.5c     输入category名称,最大宽度是5,category>5,就会将右边多出的字符截掉,<5不会有空格
    %20.30c category名称<20补空格,并且右对齐,>30字符,就从右边交远销出的字符截掉

Log4j 入门Demo

建设maven工程

增加依赖 因为,不是Java自身提供的须要引入对应的依赖~

<!--log4j-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<!-- 为了不便测试,能够导入 JUnit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
<!-- log4j 具备操作数据库的性能,引入对应的 数据库驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

Log4jTest.Java

//阿帕奇 log4j 的包哦~
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.junit.Test;

public class Log4jTest {
    /** 入门案例 */
    @Test
    public void test(){
        //1. 应用前须要初始化配置信息,不然会报错! (理论开发是通过,配置文件; 入门案例须要引入默认的)
        BasicConfigurator.configure();
        //2. 创立日志对象
        Logger logger = Logger.getLogger(Log4jTest.class);//不仅反对 惟一字符 还有 类对象

        //3. 输入日志信息!
        System.out.println("log4j 和 JUL 的日志级别,有些不同..");
        logger.fatal("fatal 严重错误,个别会造成零碎解体并终止运行");
        logger.error("error 错误信息,不会影响零碎运行");
        logger.warn("warn 正告信息,可能会产生问题");
        logger.info("info 追踪信息,记录程序所有的流程信息");
        logger.debug("debug 调试信息,个别在开发中应用,记录程序变量参数传递信息等等");
        logger.trace("trace 追踪信息,记录程序所有的流程信息");
    }
}

Log4j 配置文件应用:

源码剖析:

  • Logger.getLogger() 进入,发现Logger 对象是又,一个LogManager对象.getLogger();创立

    进入LogManage类 发现 static{ 代码块中 } url = Loader.getResource("log4j.properties");

    类扫描读取log4j.properties 配置文件,只有 resource 资源目录下存在 log4j.properties文件,就不须要初始化配置信息了!

    static{ 代码块中 } 外部有一个OptionConverter.selectAndConfigure(..) 读取配置文件…依据文件类型,动静读取 xml properties 获取Configurator对象

  • Configurator对象用于加载,初始化 root 对象,能够通过它来,查看配置文件如何编写

    selectAndConfigure办法中, new PropertyConfigurator(); 进入

    发现:APPENDER_PREFIX RENDERER_PREFIX等很多的配置文件,属性…

rootLogger 配置文件加载默认 日志对象

log4j.properties resources 资源文件下,增加配置文件;⚙

# 设置默认的日志对象 rootLogger
# 日志级别 和 绑定的appender(能够多个)        逗号,分隔
log4j.rootLogger = trace,console
# 指定控制台日志输入的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定控制台日志输入的 layout 输入格局默认 SimpleLayout
log4j.appender.console.layout = org.apache.log4j.SimpleLayout

Log4jTest.Java

/** 配置文件加载: rootlogger对象;  */
@Test
public void test1(){
    //resources 目录下增加一个, log4j.properties
    //这样就不须要 BasicConfigurator.configure(); 加载默认配置文件了;
    
    // 开启 log4j 内置日志记录 (轻易,开启日志输入,日志框架的日志信息~
    LogLog.setInternalDebugging(true);
       
    //创立日志对象
    Logger logger = Logger.getLogger(Log4jTest.class);
    //输入日志信息!
    System.out.println("log4j 和 JUL 的日志级别,有些不同..");
    logger.fatal("fatal 严重错误,个别会造成零碎解体并终止运行");
    logger.error("error 错误信息,不会影响零碎运行");
    logger.warn("warn 正告信息,可能会产生问题");
    logger.info("info 追踪信息,记录程序所有的流程信息");
    logger.debug("debug 调试信息,个别在开发中应用,记录程序变量参数传递信息等等");
    logger.trace("trace 追踪信息,记录程序所有的流程信息");
}
log4j 和 JUL 的日志级别,有些不同..
FATAL - fatal 严重错误,个别会造成零碎解体并终止运行
ERROR - error 错误信息,不会影响零碎运行
WARN - warn 正告信息,可能会产生问题
INFO - info 追踪信息,记录程序所有的流程信息
DEBUG - debug 调试信息,个别在开发中应用,记录程序变量参数传递信息等等
TRACE - trace 追踪信息,记录程序所有的流程信息

尝试,批改配置文件,运行 test1()

# 指定控制台日志输入的 layout 输入格局默认 SimpleLayout (HTMLLayout(以HTML展现日志信息) / xml.XMLLayout(以xml模式展现数据) / PatternLayout(自定义罕用!)
log4j.appender.console.layout = org.apache.log4j.HTMLLayout

输入的日志就会以,html 模式进行展现📺

PatternLayout 自定义,日志格局输入:

log4j.properties 追加

# 设置默认的日志对象 rootLogger
# 日志级别 和 绑定的appender(能够多个)        逗号,分隔
log4j.rootLogger = trace,console
# 指定控制台日志输入的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定控制台日志输入的 layout 输入格局默认 SimpleLayout (HTMLLayout / xml.XMLLayout / PatternLayout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 指定音讯格局的内容   layout.conversionPattern
log4j.appender.console.layout.conversionPattern = [%-10p]%r  %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n
    # 设置,数据格式:
    # [%-10p]:      [数据长度10且居左] %r输入自利用启动到输入该log信息消耗的毫秒数
    # %r:           输入自利用启动到输入该 log 信息消耗的毫秒数
    # %l:           输入日志工夫产生的地位,包含类名、线程、及在代码中的行数
    # %d:           输入服务器以后工夫,默认为 ISO8601,也能够指定格局

前期这样的格局,公司都是有固定的标准的曾经配置好了 理解即可!

输入日志,保留文件:file

log4j.properties 追加

设置 log4j.rootLogger = trace,console,file 绑定对应的 appender

# 日志级别 和 绑定的appender(能够多个)        逗号,分隔
log4j.rootLogger = trace,console,file

# 日志文件输入的 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
# 指定日志文件保留门路 (须要手动指定,日志文件保留到磁盘...不然出错,自己这回默认我的项目的盘符 D盘,没有文件创建文件~
log4j.appender.file.file = /logs/log4j.log
# 指定日志文件的字符集
log4j.appender.file.encoding = UTF-8

运行test1()

log4j 不像JUL ,数据默就追加了~

依照文件大小拆分 rollingFile

log4j.properties 追加

设置 log4j.rootLogger = trace,console,rollingFile 绑定对应的 appender

# 日志级别 和 绑定的appender(能够多个)        逗号,分隔
log4j.rootLogger = trace,console,rollingFile

# 依照文件大小拆分的 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
# 指定日志文件内容的大小( 没个文件1mb 产生新的日志文件~
log4j.appender.rollingFile.maxFileSize = 1MB 
# 指定日志文件的数量(最多产生 10个日志文件, log4j.log log4j2.log log4j3.log, 超出文件个数,旧的文件会新文件被笼罩
log4j.appender.rollingFile.maxBackupIndex = 10

Log4jTest.Java 循环1w次查看后果~

for (int i = 0; i < 10000; i++) {
    logger.fatal("fatal 严重错误,个别会造成零碎解体并终止运行");
    logger.error("error 错误信息,不会影响零碎运行");
    logger.warn("warn 正告信息,可能会产生问题");
    logger.info("info 追踪信息,记录程序所有的流程信息");
    logger.debug("debug 调试信息,个别在开发中应用,记录程序变量参数传递信息等等");
    logger.trace("trace 追踪信息,记录程序所有的流程信息");
}

依照工夫规定拆分的 dailyFile

log4j.properties 追加

设置 log4j.rootLogger = trace,console,dailyFile 绑定对应的 appender

# 日志级别 和 绑定的appender(能够多个)        逗号,分隔
log4j.rootLogger = trace,console,dailyFile

# 依照工夫规定拆分的 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

运行:test1()

日志文件 写入数据库:

创立对应的数据库, 数据库表脚本 .sql

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`)
);

log4j.properties 追加

设置 log4j.rootLogger = trace,console,dailyFile 绑定对应的 appender

# 日志级别 和 绑定的appender(能够多个)        逗号,分隔
log4j.rootLogger = trace,console,logDB

#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
# 设置数据库: 连贯信息还有操作的数据库,(自己的数据库的 log~
log4j.appender.logDB.URL=jdbc:mysql://localhost:3306/log
# 数据库 用户名
log4j.appender.logDB.User=root
# 数据库 明码
log4j.appender.logDB.Password=ok
# 设置入库的语句, %? 对应列要存的数据,参考,PatternLayout
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')

运行test1()

自定义 logger 对象:

作用:对于多个生成环境,须要记录的是不同的日志…能够应用 自定义日志对象类设置~

log4j.properties

# 自定义 logger 对象设置 (输入日志级别更改为了 info, appender还会默认继承 rootlogger
log4j.logger.com.wsm = info,console

Log4jTest.Java

/** 自定义 logger 对象 */
@Test
public void test2(){
    //获取自定义日志对象~
    Logger logger = Logger.getLogger("com.wsm");
    //输入~
    logger.fatal("fatal 严重错误,个别会造成零碎解体并终止运行");
    logger.error("error 错误信息,不会影响零碎运行");
    logger.warn("warn 正告信息,可能会产生问题");
    logger.info("info 追踪信息,记录程序所有的流程信息");
    logger.debug("debug 调试信息,个别在开发中应用,记录程序变量参数传递信息等等");
    logger.trace("trace 追踪信息,记录程序所有的流程信息");
}

只展现大于等于 info 的日志级别~

[FATAL     ]0  com.wsm.Log4jTest.test2(Log4jTest.java:57) 2021-12-19 22:35:11.558 fatal 严重错误,个别会造成零碎解体并终止运行
[ERROR     ]3  com.wsm.Log4jTest.test2(Log4jTest.java:58) 2021-12-19 22:35:11.561 error 错误信息,不会影响零碎运行
[WARN      ]3  com.wsm.Log4jTest.test2(Log4jTest.java:59) 2021-12-19 22:35:11.561 warn 正告信息,可能会产生问题
[INFO      ]3  com.wsm.Log4jTest.test2(Log4jTest.java:60) 2021-12-19 22:35:11.561 info 追踪信息,记录程序所有的流程信息

JCL

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

  • 它为 “所有的Java日志实现”提供一个对立的接口”,它本身也提供一个日志的实现,然而性能十分常弱 SimpleLog简直被淘汰了, 个别不会独自应用它

    他容许开发人员应用不同的具体日志实现工具: Log4j, Jdk 自带的日志(JUL)

  • JCL 有两个根本的抽象类:Log(根本记录器)LogFactory(负责创立Log实例)

JCL入门

创立Maven工程

增加 pom.xml依赖

<dependencies>
    <!-- JCL的日志依赖 -->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <!-- junit的依赖 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!-- log4j的依赖! -->
    <!--         <dependency>-->
    <!--             <groupId>log4j</groupId>-->
    <!--             <artifactId>log4j</artifactId>-->
    <!--             <version>1.2.17</version>-->
    <!--         </dependency>-->
</dependencies>

JCLTest.Java

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
/** JUL入门案例~ */
public class JCLTest {
    @Test
    public void test(){
        // 获取 log日志记录器对象, JCL通过一个日志工厂 获取一个日志对象;
        Log log = LogFactory.getLog(com.wsm.JCLTest.class);
        // 日志记录输入
        log.info("hello jcl");
    }
}

输入了和 JUL一样的日志数据, 当没有引入 log4j依赖时候 JCL默认就通过JUL实现,则应用log4j

十二月 19, 2021 11:02:59 下午 com.wsm.JCLTest test
信息: hello jcl

切换 log4j日志框架

解开log4j的依赖配置 增加:依赖,配置文件 log4j.properties

# 设置默认的日志对象 rootLogger
# 日志级别 和 绑定的appender(能够多个)        逗号,分隔
log4j.rootLogger = trace,console
# 指定控制台日志输入的 appender
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定控制台日志输入的 layout 输入格局默认 SimpleLayout (HTMLLayout / xml.XMLLayout / PatternLayout
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 指定音讯格局的内容   layout.conversionPattern
log4j.appender.console.layout.conversionPattern = [%-10p]%r  %l %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n

运行test(),是log4j的输入形式

[INFO      ]1  com.wsm.JCLTest.test(JCLTest.java:13) 2021-12-19 23:17:35.636 hello jcl

总结:⭐

JCL 是一个日志门面,随着日志的应用频繁市面上又有很多个不同的日志框架,开发者须要同时把握多个日志框架(切实不是很敌对~

阿帕奇,就退出了一个日志门面,来对立实现了接口,JUL 和 log4j都对其进行了实现。

益处,开发者只须要学习一个日志框架的应用,即便前面更改日志框架,也不会对代码产生任何影响~👍 只须要替换一个依赖即可!

  • JCL 是由默认实现的SimpleLog,但很少有人应用~
  • JUL 是Java自带的,所以不须要引入依赖,就默认应用了JUL实现!

但,JCL 对于不同日志框架的切换实现…

是以一种,硬编码模式实现,之兼容了大量的日志框架,如果前面须要引入新的日志,还须要更改配置文件~

所以,前面又呈现了一种新的日志门面 Slf4j 它能够兼容目前市面上所有/以及将来的 日志框架

Slf4j

简略日志门面Simple Logging Facade For Java

  • SLF4J次要是为了给Java日志拜访提供一套规范、标准 的API框架,其次要意义在于提供接口,具体的实现能够交由其余日志框架
  • 当然slf4j本人也提供了性能较为简单的实现然而个别很少用到
  • 当初很多的Java我的项目都应用,slf4j-api作为门面
  • 配上具体的实现框架(log4j、logback等),两头应用桥接器实现桥接
  • 官网地址

SLF4J日志门面次要提供两大性能:

  • 日志框架的绑定
  • 日志框架的桥接

SLF4J入门

创立Maven工程:

导入对应的依赖:

<!-- slf4j 日志门面 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.26</version>
</dependency>

Slf4jTest.Java

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** Slf4j入门案例 */
public class Slf4jTest {
    //申明全局的 日志对象
    public final static Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);
    /** 简略入门 */
    @Test
    public void Test(){
        // 日志输入(默认输入至,控制台 info级别;
        LOGGER.error("error");
        LOGGER.warn("wring");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");

        // 应用占位符输入日志信息
        String name = "wsm";
        Integer age = 540;
        LOGGER.info("用户:{},{}",name,age);

        // 将零碎的异样信息输入
        try {
            int i = 1/0;
        } catch (Exception e) {
            // e.printStackTrace();  这样程序就不会,打印Java的 堆栈一推谬误了...
            LOGGER.error("出现异常:",e);
        }
    }
}
[main] ERROR com.wsm.Slf4jTest - error
[main] WARN com.wsm.Slf4jTest - wring
[main] INFO com.wsm.Slf4jTest - info
[main] INFO com.wsm.Slf4jTest - 用户:wsm,540
[main] ERROR com.wsm.Slf4jTest - 出现异常:
java.lang.ArithmeticException: / by zero
    at com.wsm.Slf4jTest.Test(Slf4jTest.java:29) 
#省略上面更多,异样信息~    

日志框架的绑定

Slf4j 作为一个弱小的日志门面,能够在不扭转代码的状况下,实现切换不同的日志框架…反对不同的日志框架的绑定!

上图,是Slf4j 官网的图片,与各个框架进行绑定的介绍:

application 指定是应用程序 SLF4J API 就是Slf4j日志门面 从左往右——>1-6

  • Slf4j无绑定我的项目只是单纯的引入了 日志门面...
  • ② ⑤ ⑥ 别离是 logback slf4j内置实现 log4j2

    都是在,slf4j门面之后呈现的,因而都对其进行了实现,之间导入 Slf4j日志门面依赖对应实现依赖即可,主动绑定实现

  • ③ ④ 是在 slf4j之前开发的,没有间接的实现 slf4j门面

    所以在,应用之前还须要导入对应的 Adaptation适配器来进适配.

    应用 slf4j门面办法,外部适配器在调用,JUL / log4j 的办法~

接下来,粗略介绍一下各个日志框架的绑定:slf4j内置实现,入门案例就是

Logback 绑定

因为,曾经默认实现类,slf4j 间接引入依赖即可… 别忘记正文,slf4j内置实现依赖

  • 这里不具体,介绍Logback应用.
<!--logback 日志实现-->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

slf4j 空实现:

当咱们还不能确定,应用什么日志框架来实现时候,能够引入 slf4j-nop

它是 slf4j的一种空实现,因为如果不引入 slf4j的实现,会报错Defaulting to no-operation (NOP) logger implementation

  • 正文其它依赖 日志实习依赖 运行程序异样~
  • 增加 slf4j-nop 依赖,运行test();
<!-- nop 日志开关: slf4j的一种空实现~ -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-nop</artifactId>
    <version>1.7.25</version>
</dependency>
  • 运行,不出异样,但无后果…

Log4j 绑定

因为,Log4j诞生早于,Slf4j所以并没有间接对其进行实现,slf4j 与 log4j 进行绑定还须要增加一个 适配器

正文其它实现依赖…

<!--绑定 log4j 日志实现,须要导入适配器 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.12</version>
</dependency>
<!-- log4j依赖 -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

运行 test()报错:log4j:WARN No appenders could be found for logger (com.wsm.Slf4jTest).

  • 须要引入对应的 appenders 须要,增加对应的log4j.properties配置文件~

JUL 绑定

同上,JUL也须要增加一个适配器 因为JUL 是JDk 自带的,所以,只须要引入一个 适配器依赖即可!

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

JUL ,JDK内有默认的 配置文件.

总结:⭐

应用slf4j的日志绑定流程:

  • 增加slf4j-api的依赖
  • 应用slf4j的API在我的项目中进行对立的日志记录
  • 绑定具体的日志实现框架:

    绑定曾经实现了slf4j的日志框架,间接增加对应依赖

    绑定没有实现slf4j的日志框架,先增加日志的适配器,再增加实现类的依赖

  • slf4j有且仅有一个日志实现框架的绑定(如果呈现多个默认应用第一个依赖日志实现

日志框架的桥接Bridging

假如,忽然让你保护一个比拟老的我的项目。老板让你把原先我的项目的日志框架 log4j 换成 logback

  • 咋办,两个框架一个属于 slf4j门面 一个属于 JCL门面 间接替换jar包,必定不行!
  • 那怎么办❓ 移除依赖,批改代码❓ 那真的太狗血了🐕!

还好,slf4j 提供了桥接,技术 反对开发者引入对应的日志的 桥接器 来实现配置!

log4j 切换 logback Demo

Slf4j模块,增加 Log4jTest.Java

import org.apache.log4j.Logger;
import org.junit.Test;

public class Log4jTest {
    // 定义 log4j 日志记录器
    public static final Logger LOGGER = Logger.getLogger(Log4jTest.class);
    // 测试桥接器
    @Test
    public void test01()throws Exception{
        LOGGER.info("hello lgo4j");
    }
}

别忘记了 log4j 须要引入配置文件log4j.properties, 运行 test01();

15:52:04.308 [main] INFO com.itheima.Log4jTest - hello lgo4j

下面是 log4j的代码和输入,上面是,切换的logback的代码输入..

引入依赖:正文log4j的依赖...

<!-- Slf4j的桥接技术 -->
<!-- 将log4j 日志框架,不更改代码清空下,替换为 logback: 正文掉所有的依赖,只保留 JUnit -->

<!-- log4j的所需依赖 -->
<!--        <dependency>-->
<!--            <groupId>log4j</groupId>-->
<!--            <artifactId>log4j</artifactId>-->
<!--            <version>1.2.17</version>-->
<!--        </dependency>-->

<!-- 正文,log4j 的实现依赖; -->
    <!--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>
十二月 20, 2021 4:48:50 下午 com.wsm.log4jTest test01
信息: hello lgo4j

发现代码不变,输入的形式曾经变成了 logback

起因:log4j-over-slf4j桥接器,外部调用了 Slf4j的办法,相当于进行了一个重定向~

留神⚠:

log4j-over-slf4j.jarslf4j-log4j12.jar不能同时呈现

jcl-over-slf4j.jarslf4j-jcl.jar不能同时呈现

jul-to-slf4j.jarslf4j-jdk14.jar不能同时呈现

日志桥接器,不能和 日志的实现依赖同时呈现,因为,日志桥接器 底层还是去调用Slf4j的接口办法. Slf4j 回去调用日志实现

而如果同时引入了,雷同的 桥接器 日志实现,日志实现 –调用–> 桥接器 –调用–> Slf4j –调用–> 日志实现 陷入死循环,堆栈溢出!

Logback

Logback是由log4j创始人设计的另一个开源日志组件:官网地址

Logback次要分为三个模块:

  • logback-core:其它两个模块的根底模块, Maven具备继承个性,引入了 classic/access 就默认具备 core了
  • logback-classic:它是log4j的一个改进版本,同时它残缺实现了slf4j API
  • logback-access:拜访模块与Servlet容器集成提供通过Http来拜访日志的性能 用于Tomcat/Jetty 日志间接输入到对应的服务器日志中去!

Logback 入门案例:

创立Maven 工程

引入 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>
<!--junti 单元测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

LogbackTest.Java

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** logBack日志入门案例 */
public class LogbackTest {
    //创立全局的 logger 对象
    public static final Logger LOGGER = LoggerFactory.getLogger(LogbackTest.class);
    @Test
    public void test(){
        //调用 Slf4j 接口实现日志输入;
        // logback 一共有五个日志级别,的默认级别是, debug
        LOGGER.error("error");
        LOGGER.warn("wring");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }
}

存在,默认的配置 rootLogger 默认:控制台输入, 日志级别debug 😶

10:28:18.657 [main] ERROR com.wsm.LogbackTest - error
10:28:18.662 [main] WARN com.wsm.LogbackTest - wring
10:28:18.662 [main] INFO com.wsm.LogbackTest - info
10:28:18.662 [main] DEBUG com.wsm.LogbackTest - debug

Logback 介绍:

logback会顺次读取以下类型配置文件:

  • logback.groovy
  • logback-test.xml
  • logback.xml 如果均不存在会采纳默认配置 在 resources 资源目录下,创立任意一个配置文件即可

xml 格局,不便读取,和操作, 应用也绝对比拟多,本篇介绍logback.xml 配置文件编写✍~

logback组件之间的关系:

和其它日志框架,也大抵一样,日志对象 记录器 日志输入格局

Logger 日志记录器对象

  • 依据context环境(配置文件LoggerFactory.getLogger()生成一个 日志记录器对象,次要用于寄存日志对象,定义日志类型、级别
  • Logger 能够被调配级别:

    级别包含:TRACE、DEBUG、INFO、WARN 和 ERROR 定义于ch.qos.logback.classic.Level

Appender 记录器

  • 用于指定日志输入的目的地,目的地能够是控制台、文件、数据库等等

Layout 日志输入格局

  • 负责把事件转换成字符串,格式化的日志信息的输入,logback中Layout对象被封 装在encoder中

Logback 配置⚙:

配置文件入门:

resources 资源目录下增加: logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- configuration xml的根节点,存在三个属性;
        scan:       当此属性设置为true时,配置文件如果产生扭转,将会被从新加载,默认值为true
        scanPeriod: 设置监测配置文件是否有批改的工夫距离,如果没有给出工夫单位,默认单位是毫秒,当scan为true时,此属性失效;
        debug:      当此属性设置为true时,将打印出logback外部日志信息,实时查看logback运行状态。默认值为false;
    -->
<configuration scan="true" scanPeriod="60 seconds" debug="false"> 
    <!-- contextName:
            用来设置上下文名称,每个logger都关联到logger上下文,默认上下文名称为default;
            置成其余名字,用于辨别不同应用程序的记录;
    -->
    <contextName>mylog</contextName>

    <!-- property:
            用来定义变量值,它有两个属性name和value,通过<property>定义的值 能够被配置文件上下文援用, ${name名} 来应用变量
            name: 变量的名称
            value: 的值时变量定义的值
    -->
    <!--定义日志文件保留门路属性-->
    <property name="log_dir" value="/logs"></property>
    <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换行  -->
</configuration> 

控制台日志输入的 appender

logback.xml追加:

<!-- 控制台日志输入的 appender 它有两个必要属性name和class;
        name指定appender名称
        class指定appender的全限定名,不同的 输入Appender 又有不同的子元素设置:
-->
<!-- ConsoleAppender 把日志输入到控制台;
        <encoder>:  对日志进行格式化
        <target>:   Java控制台输入,字符串System.out(默认)或者System.err(红色字体
-->
<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>

<!-- rootlogger 配置:
        level:          设置日志输入级别;
        <appender-ref>: 设置rootlogger 绑定的appender,反对绑定多个~
 -->
<root level="ALL">
    <appender-ref ref="console"/>    <!-- 依据 appender 的name 属性进行关联; -->
</root>

控制台:输入对应格局的 system.err 输入的红色字体~

[ERROR] 2021-12-21 16:54:09.008 com.itheima.LogbackTest testQuick 16 [main] error
[WARN ] 2021-12-21 16:54:09.012 com.itheima.LogbackTest testQuick 17 [main] wring
[INFO ] 2021-12-21 16:54:09.012 com.itheima.LogbackTest testQuick 18 [main] info
[DEBUG] 2021-12-21 16:54:09.012 com.itheima.LogbackTest testQuick 19 [main] debug
[TRACE] 2021-12-21 16:54:09.012 com.itheima.LogbackTest testQuick 20 [main] trace

输入日志文件:

logback.xml

<!-- 日志文件输入的 appender-->
<!-- FileAppender 把日志增加到文件;
        <file>:     被写入的文件名,能够是绝对目录,也能够是相对目录,如果下级目录不存在会主动创立,没有默认值;
        <append>:   如果是 true 日志被追加到文件结尾 (默认 true;
        <encoder>:  对记录事件进行格式化;
        <prudent>:  如果是 true,日志会被平安的写入文件,即便其余的FileAppender也在向此文件做写入操作; (效率低默认是 false;
 -->
<appender name="file" class="ch.qos.logback.core.FileAppender">
    <!--日志文件保留门路: ${log_dir}引入下面的,全局变量-->
    <file>${log_dir}/logback.log</file>
    <!--日志音讯格局配置-->
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <pattern>${pattern}</pattern>
    </encoder>
</appender>

<!-- rootlogger 配置:
        level:          设置日志输入级别;
        <appender-ref>: 设置rootlogger 绑定的appender,反对绑定多个~
 -->
<root level="ALL">
    <appender-ref ref="console"/>    <!-- 依据 appender 的name 属性进行关联; -->
    <appender-ref ref="file"/>
</root>

输入文件 html

logback.xml

<!--html 格局日志文件输入 appender-->
<appender name="htmlFile" class="ch.qos.logback.core.FileAppender">
    <!--日志文件保留门路-->
    <file>${log_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.SSS}%c%M%L%thread%m</pattern>
        </layout>
    </encoder>
</appender>

<!-- 设置:rootlogger 配置 -->

日志拆分和归档压缩 / 异步日志

logback.xml

<!-- 日志拆分和归档压缩的 appender 对象-->
<!-- RollingFileAppender
        省略与 FileAppender 雷同的元素;
        <rollingPolicy>:    当产生滚动时,决定RollingFileAppender的行为,波及文件挪动和重命名。属性class定义具体的滚动策略类;
        <prudent>:          当为true时,不反对FixedWindowRollingPolicy,
 -->
<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 依照工夫和压缩格局申明拆分的文件名-->
        <fileNamePattern>${log_dir}/rolling.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
        <!--maxFileSize 设置依照文件大小拆分: -->
        <maxFileSize>1MB</maxFileSize>
        <!-- maxHistory 可选节点,管制保留的归档文件的最大数量,超出数量就删除旧文件; -->
    </rollingPolicy>

    <!--日志级别过滤器-->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <!--日志过滤规定
               <level>:         设置过滤级别
               <onMatch>:       用于配置合乎过滤条件的操作
               <onMismatch>:    用于配置不合乎过滤条件的操作

            onMatch/onMismatch  执行一个过滤器会有返回个枚举值: DENY,NEUTRAL,ACCEPT
                DENY            日志将立刻被摈弃不再通过其余过滤器;
                NEUTRAL         有序列表里的下个过滤器过接着解决日志;
                ACCEPT          日志会被立刻解决,不再通过残余过滤器;
         -->
        <level>ERROR</level>
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
</appender>


<!-- 异步日志
       appender反对通过, appender-ref 间接援用其它的 appender属性~
 -->
<!-- AsyncAppender
        写出文件,时候会开启一个线程,以异步模式写出日志文件~
 -->
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
    <!--指定某个具体的 appender-->
    <appender-ref ref="rollFile"/>
</appender>

<!-- 设置:rootlogger 配置 -->

为了可能看到成果, 循环 1w次日志打印~

自定义 looger 对象

logback.xml

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

name 设置我的项目的 包名.包名.xxx 该包下,会默认具备 自定义logger对象, additivity能够用于设置是否继承 rootlogger

[ERROR] 2021-12-21 23:20:13.702 com.wsm.LogbackTest test 16 [main] error
[WARN ] 2021-12-21 23:20:13.705 com.wsm.LogbackTest test 17 [main] wring
[INFO ] 2021-12-21 23:20:13.705 com.wsm.LogbackTest test 18 [main] info

输入级别变成了 info

Log4j2:

Apache Log4j 2是对Log4j的升级版 官网地址)

参考了logback的一些优良的设计,并且修复了一些问题,因而带 来了一些重大的晋升:

  • 异样解决

    在logback中,Appender中的异样不会被利用感知到,然而在log4j2中,提供了一些异 常解决机制;

  • 性能晋升

    log4j2相较于log4j 和logback都具备很显著的性能晋升 据说提供了十几倍!

  • 主动重载配置

    参考了logback的设计,当然会提供主动刷新参数配置,最实用的就是咱们在生产 上能够动静的批改日志的级别而不须要重启利用;

  • 无垃圾机制

    log4j2在大部分状况下,都能够应用其设计的一套无垃圾机制,防止频繁的日志收集,导致的jvm gc;

Log4j2入门案例:

创立一个 Maven 工程:

引入依赖:pom.xml

<dependencies>
    <!--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>
    <!--junit 单元测试-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

编写入门案例:Log4j2Test.Java

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;

/** log4j2 疾速入门 */
public class Log4j2Test {
    // 定义日志记录器对象 LogManager.getLogger();
    public static final Logger LOGGER = LogManager.getLogger(Log4j2Test.class);
    /** 疾速入门 */
    @Test
    public void test(){
        //log4j2 默认日志级别是 error;
        LOGGER.fatal("fatal");
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("inf");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }
}
ERROR StatusLogger No Log4j 2 configuration file found. Using default configuration (logging only errors to the console), or user programmatically provided configurations. Set system property 'log4j2.debug' to show Log4j 2 internal initialization logging. See https://logging.apache.org/log4j/2.x/manual/configuration.html for instructions on how to configure Log4j 2
09:38:36.825 [main] FATAL com.wsm.Log4j2Test - fatal
09:38:36.838 [main] ERROR com.wsm.Log4j2Test - error

输入后果,提醒没有配置对应的 配置文件!,能够在 resources资源目录下创立一个 log4j2.xml的配置文件 log4j2 的配置文件 和 logback大致相同;

尽管输入后果,有正告信息,然而仍然失常的打印了日志:log4j2的默认日志级别是 error

Log4j2 配置文件:

以后 resources 资源目录下创立一个 log4j2.xml 与 logback.xml配置文件大致相同~

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">/logs</property>
    </properties>

    <!--日志解决,设置appenders 集-->
    <Appenders>
        <!--控制台输入 appender
                target:    控制台输入的形式 System.err / System.out    
        -->
        <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>
        
        <!--应用随机读写流的日志文件输入 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
                filePattern:    拆分文件名,$${date:yyyy-MM-dd}每天生成一个文件夹, %i 数字排序~
        -->
        <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>
        <!--应用 rootLogger 配置 日志级别 level="trace"-->
        <Root level="trace">
            <!--指定日志应用的处理器-->
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>
15:41:00.743 [main] [FATAL] com.wsm.Log4j2Test:15 --- fatal
15:41:00.754 [main] [ERROR] com.wsm.Log4j2Test:16 --- error
15:41:00.754 [main] [WARN ] com.wsm.Log4j2Test:17 --- warn
15:41:00.755 [main] [INFO ] com.wsm.Log4j2Test:18 --- inf
15:41:00.755 [main] [DEBUG] com.wsm.Log4j2Test:19 --- debug
15:41:00.755 [main] [TRACE] com.wsm.Log4j2Test:20 --- trace

间接输入就没有,异样信息了, 而且日志级别是 trace

Slf4j + log4j2

目前市面上最支流的日志门面就是SLF4J,Log4j2也是日志门面 也有本人对应的实现,现性能十分弱小,性能优越

  • 尽管,Slf4j 和 logback 是同一个开发者,然而log4j2 功能强大!
  • Slf4j + Log4j2应该是将来的大势所趋 可最近出的一个bug 预计,也危险了☠

Slf4j + log4j2 整合案例:

pom.xml 增加依赖:

  • 应用 slf4j 作为 日志门面,须要应用 log4j2的适配器:
<!--应用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>

从新编写 slf4j 的接口调用办法! Slf4jTest.Java

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** slf4j + log4j2整合 */
public class Slf4jTest {
    //slf4j 的获取,日志对象的办法;
    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");
    }
}
15:52:17.495 [main] [ERROR] com.wsm.Slf4jTest:16 --- error
15:52:17.500 [main] [WARN ] com.wsm.Slf4jTest:17 --- wring
15:52:17.501 [main] [INFO ] com.wsm.Slf4jTest:18 --- info
15:52:17.501 [main] [DEBUG] com.wsm.Slf4jTest:19 --- debug
15:52:17.501 [main] [TRACE] com.wsm.Slf4jTest:20 --- trace

当初输入的就是 Slf4j 的,数据了,然而它底层依然是 log4j2 所以 log4j2.xml配置文件依然有成果😀

异步日志:

log4j2最大的特点就是异步日志,其性能的晋升次要也是从异步日志中受害 大大的进步了程序运行效率;

Log4j2提供了两种实现异步日志的形式:

  • 一个是通过AsyncAppender 效率并没有特地大晋升
  • 一个是通过AsyncLogger 罕用

两者都须要增加 依赖:

<!--异步日志依赖-->
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.4</version>
</dependency>

AsyncAppender 异步记录器;

AsyncAppender 形式就间接在 log4j2.xml 增加对应的适配;

 <Appenders>       
     <!-- 异步输入 appender -->
     <Async name="Async">
         <!-- 指定须要被异步执行的适配器: ref="适配器name" -->
         <AppenderRef ref="Console"/>
         <!-- <AppenderRef ref="file"/> 能够多个...-->
     </Async>
 </Appenders>

AsyncLogger 异步对象;

AsyncLogger 形式, 存在两种形式 全局异步和混合异步

  • 全局异步

    就是,所有的日志都异步的记录,在配置文件上不必做任何改变, 只须要增加一个 log4j2.component.properties 配置文件;

  • 混合异步

    你能够在利用中同时应用同步日志和异步日志,这使得日志的配置形式更加灵便

全局异步对象:

只须要在 resources 资源目录下,创立一个配置文件 log4j2.component.properties 即可:

Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

混合异步对象:

就是在 log4j2.xml 文件,< Loggers > 中定义,对应的包门路下类对象.. 设置成异步执行

<!--logger 定义-->
<Loggers>
    <!-- 自定义异步 logger 对象 -->
    <AsyncLogger name="com.wsm" level="trace" includeLocation="false" additivity="false">
        <AppenderRef ref="Console"/>
    </AsyncLogger>
</Loggers>

无垃圾模式🚮:

垃圾收集暂停是提早峰值的常见起因,并且对于许多零碎而言,破费大量精力来管制这些暂停

  • 许多的日志框架, 在稳态日志记录期间调配长期对象

    日志事件对象,字符串, 字符数组,字节数组等。这会对垃圾收集器造成压力并减少GC暂停产生的频率;

  • Log4j2 在2.6 版本后, 默认引入了 无垃圾运行模式 尽量的,不应用长期对象
  • 2.6之后就默认应用了 无垃圾模式

    log4j2.enableThreadlocals 设置为 true, 对象存储在 ThreadLocal字段中并从新应用,否则将为每个日志事件创立新对象 非Web应用程序的默认值

    log4j2.enableDirectEncoders 设置为 true 日志事件转换为文本,则将此文本转换 为字节而不创立长期对象 留神: 因为应用共享缓冲区上的同步 所以倡议应用异步记录器!

    所以,只有降级到2.6 版本即可~

总结:

log4j2 日志框架性能,当于其它框架高很多,次要在 2.6之后引入了两个概念: 异步日志 无垃圾模式

SpringBoot 整合日志框架:

Idea Spring lnitializr Spring结构器 创立一个 SpringBoot 工程:

SpringBoot 底层默认应用:SLF4J作为日志门面 logback作为日志实现

Maven依赖关系图:

也引入了JUL 和 log4j 的桥接, 能够间接转换!

SpringBoot日志应用

test 模块下:DemoApplicationTests.Java

import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoApplicationTests {
    //创立日志记录器   导报: slf4j
    public static final Logger LOGGER =  LoggerFactory.getLogger(DemoApplicationTests.class);
    @Test
    void contextLoads() {
        // 打印日志信息
        LOGGER.error("error");
        LOGGER.warn("warn");
        LOGGER.info("info");
        LOGGER.debug("debug");
        LOGGER.trace("trace");
    }
}

省略…. 以上SpringBoot 日志

2021-12-23 15:12:08.771 ERROR 18772 --- [           main] com.wsm.demo.DemoApplicationTests        : error
2021-12-23 15:12:08.771  WARN 18772 --- [           main] com.wsm.demo.DemoApplicationTests        : warn
2021-12-23 15:12:08.771  INFO 18772 --- [           main] com.wsm.demo.DemoApplicationTests        : info

能够看到,SpringBoot 默认集成了 Slf4j logback 默认日志级别是 info 还能够通过,SpringBoot application.properties/yml 配置进行配置文件的更改!

SpringBoot 配置文件 ⚙

SpringBoot 能够通过,配置文件,进行简略的批改日志配置:

application.properties 这里应用 pro 能够自行替换为 yml

# 指定自定义 logger 对象日志级别,间接设置根目录级别;
logging.level.com.wsm=trace
# 指定控制台输入音讯格局
logging.pattern.console=[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c [%thread]===== %msg %n
# 指定寄存日志文件的具体门路
# logging.file 曾经过期的,因为这样设置不方便管理..
# logging.file=/logs/springboot.log
# logging.file.path 能够指定日志文件寄存的目录,默认的文件名 spring.log  具备更好的辨识度了;
logging.file.path=/logs/springboot/
# 指定日志文件音讯格局
logging.pattern.file=[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c [%thread]===== %msg %n
# Springboot 提供的配置文件不够...只能示意根本的日志配置!

SpringBoot 解析配置文件:

Springboot 默认的配置文件,仅反对根本的日志配置

给类门路下放上每个日志框架本人的配置文件;SpringBoot就不应用默认配置的了

日志框架 配置文件
Logback logback-spring.xml 或 logback.xml
Log4j2 log4j2-spring.xml 或 log4j2.xml
JUL logging.properties

logback-spring.xml 举例子:

<?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>: SpringBoot 反对应用这样的对日志进行设置不同的场景~ -->
            <!-- 开发环境 -->
            <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.wsm" level="info" additivity="false">
        <appender-ref ref="console"/>
    </logger>
</configuration>

application.properties 能够动静调节,生产环境pro 和 开发环境dev

# 指定我的项目应用的具体环境
spring.profiles.active=pro

来回切换环境, 查看运行成果😀

dev
[ERROR] 2021-12-23 15:51:26.148 com.wsm.demo.DemoApplicationTests contextLoads 15 [main] -------- error 
[WARN ] 2021-12-23 15:51:26.149 com.wsm.demo.DemoApplicationTests contextLoads 16 [main] -------- warn 
[INFO ] 2021-12-23 15:51:26.149 com.wsm.demo.DemoApplicationTests contextLoads 17 [main] -------- info 
[DEBUG] 2021-12-23 15:51:26.149 com.wsm.demo.DemoApplicationTests contextLoads 18 [main] -------- debug 
[TRACE] 2021-12-23 15:51:26.149 com.wsm.demo.DemoApplicationTests contextLoads 19 [main] -------- trace 

pro
[ERROR] 2021-12-23 15:52:03.623 com.wsm.demo.DemoApplicationTests contextLoads 15 [main] xxxxxxxx error 
[WARN ] 2021-12-23 15:52:03.623 com.wsm.demo.DemoApplicationTests contextLoads 16 [main] xxxxxxxx warn 
[INFO ] 2021-12-23 15:52:03.623 com.wsm.demo.DemoApplicationTests contextLoads 17 [main] xxxxxxxx info 
[DEBUG] 2021-12-23 15:52:03.623 com.wsm.demo.DemoApplicationTests contextLoads 18 [main] xxxxxxxx debug 
[TRACE] 2021-12-23 15:52:03.623 com.wsm.demo.DemoApplicationTests contextLoads 19 [main] xxxxxxxx trace

切换日志 为 log4j2

首先,移除logback 实现依赖,增加log4j2 的依赖~

增加 log4j2的依赖配置:pom.xml

<dependencies>
    <!-- SpringBoot web starter 的集中依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <!--idea 执行后,排除 logback 日志实现-->
        <exclusions>
            <exclusion>
                <artifactId>spring-boot-starter-logging</artifactId>
                <groupId>org.springframework.boot</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <!-- SpringBoot test starter 的集中依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!--应用 log4j2 的日志启动器-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-log4j2</artifactId>
    </dependency>
</dependencies>

输入日志信息就是 log4j2 的信息了

[ERROR] 2021-12-23 16:04:54 com.wsm.demo.DemoApplicationTests [main]===== error 
[WARN ] 2021-12-23 16:04:54 com.wsm.demo.DemoApplicationTests [main]===== warn 
[INFO ] 2021-12-23 16:04:54 com.wsm.demo.DemoApplicationTests [main]===== info 
[DEBUG] 2021-12-23 16:04:54 com.wsm.demo.DemoApplicationTests [main]===== debug 
[TRACE] 2021-12-23 16:04:54 com.wsm.demo.DemoApplicationTests [main]===== trace 
[INFO ] 2021-12-23 16:04:54 com.wsm.demo.DemoApplicationTests [main]===== log4j2 info 

增加对应的配置文件: log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
    status="warn" 日志框架自身的输入日志级别
    monitorInterval="5" 主动加载配置文件的间隔时间,不低于 5 秒
-->
<Configuration status="trace" monitorInterval="5">
    <!--日志解决-->
    <Appenders>
        <!--控制台输入 appender-->
        <Console name="Console" target="SYSTEM_ERR">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L -------------- %m%n" />
        </Console>
    </Appenders>

    <!--logger 定义-->
    <Loggers>
        <!--应用 rootLogger 配置 日志级别 level="trace"-->
        <Root level="trace">
            <!--指定日志应用的处理器-->
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

从新执行输入的信息就是, 配置文件中的配置 红色字体, ------------- 距离;

ok ,到这里日志介绍就结束了, 感激观看! 😀📺

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理