[Java]日志实战:JCL,slf4j;JUL,logback,log4j,log4j2

Java日志框架历史演进及最佳实践 总结Java日志框架历史演进及Spring Boot集成日志框架 阅读详情

本文全面梳理在Java开发中常用的日志框架,注重实战(即将功能实现)。主要面向的读者是架构师养成中的初/中级开发者。由于演示的项目全部采用Maven的方式,所以本文需要的背景知识是了解Maven。所以,如果你想全面认识了解Java日志框架,这篇文章也许很适合你!

本文的主要内容:

  • 日志门面:JCL(Jakarta Commons Logging)、slf4j( Simple Logging Facade for Java)
  • 日志的实现:JUL(java util logging)、logback、log4j、log4j2

为什么需要日志:

  • 我们写代码的过程中,免不了要输出各种调试信息。在没有使用任何日志工具之前,都会使用 System.out.println 来做到。
  • 这么做直观有效,但是有一系列的缺点:不知道这句话是在哪个类,哪个线程里出来的;不知道什么时候前后两句输出间隔了多少时间;无法关闭调试信息,一旦System.out.println多了之后,到处都是输出,增加定位自己需要信息的难度等等。

目录

日志门面

JCL

slf4j

绑定日志框架

桥接旧的日志框架

日志实现

JUL

Logger之间的父子关系

日志级别Level

配置文件

log4j

三大组件

配置文件

自定义Logger

logback

配置输出到控制台

配置输出到控制台

配置输出到HTML

日志拆分&过滤器

异步日志

自定义Logger

log4j2

slf4j和log4j2组合

log4j2配置文件

异步日志


日志门面

什么是门面:日志的规范,不做具体的实现,类似于JDBC中的规范

我们为什么要使用日志门面:

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

JCL

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

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

总结:

  • JDK内置的日志门面
  • 功能弱,性能差,我们几乎不会使用它

slf4j

简单日志门面(Simple Logging Facade For Java)

  • 给Java日志访问提供一套标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交由其他日志框架,例如log4j和logback等。
  • 当然slf4j自己也提供了功能较为简单的实现,但是一般很少用到。
  • 对于一般的Java项目而言,日志框架会选择slf4j-api作为门面,配上具体的实现框架(log4j、logback等),中间使用桥接器完成桥接。

官方网站: https://www.slf4j.org/

SLF4J是目前市面上最流行的日志门面。现在的项目中,基本上都是使用SLF4J作为我们的日志系统。

SLF4J日志门面主要提供两大功能:

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

为什么要使用SLF4J作为日志门面?

  • 使用SLF4J框架,可以在部署时迁移到所需的日志记录框架。
  • SLF4J提供了对所有流行的日志框架的绑定,例如log4j,JUL,Simple logging和NOP。因此可以在部署时切换到任何这些流行的框架。
  • 无论使用哪种绑定,SLF4J都支持参数化日志记录消息。由于SLF4J将应用程序和日志记录框架分离,因此可以轻松编写独立于日志记录框架的应用程序。而无需担心用于编写应用程序的日志记录框架。
  • SLF4J提供了一个简单的Java工具,称为迁移器。使用此工具,可以迁移现有项目,这些项目使用日志框架(如Jakarta Commons Logging(JCL)或log4j或Java.util.logging(JUL))到SLF4J。
  • 总之就是,slf4j是目前最好的日志门面,大家都在用它,我们也就用它。

第一个slf4j实例

创建Maven,添加依赖:

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

public class Main {
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger("net.hackyle"); //看源码知绑定原理
        //打印日志信息
        logger.error("error");
        logger.warn("warn");
        logger.info("info");
        logger.debug("debug");
        logger.trace("trace");

        // 使用占位符输出日志信息
        String name = "jack";
        Integer age = 18;
        logger.info("用户:{},{}", name, age);

        // 将系统异常信息写入日志
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            // e.printStackTrace();
            logger.info("出现异常:", e);
        }
    }
}
//输出:
//        [main] ERROR net.hackyle - error
//        [main] WARN net.hackyle - warn
//        [main] INFO net.hackyle - info
//        [main] INFO net.hackyle - 用户:jack,18
//        [main] INFO net.hackyle - 出现异常:
//        java.lang.ArithmeticException: / by zero
//        at net.hackyle.Main.main(Main.java:23)
<!--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>

测试:

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

public class Main {
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger("net.hackyle"); //看源码知绑定原理
        //打印日志信息
        logger.error("error");
        logger.warn("warn");
        logger.info("info");
        logger.debug("debug");
        logger.trace("trace");

        // 使用占位符输出日志信息
        String name = "jack";
        Integer age = 18;
        logger.info("用户:{},{}", name, age);

        // 将系统异常信息写入日志
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            // e.printStackTrace();
            logger.info("出现异常:", e);
        }
    }
}
//输出:
//        [main] ERROR net.hackyle - error
//        [main] WARN net.hackyle - warn
//        [main] INFO net.hackyle - info
//        [main] INFO net.hackyle - 用户:jack,18
//        [main] INFO net.hackyle - 出现异常:
//        java.lang.ArithmeticException: / by zero
//        at net.hackyle.Main.main(Main.java:23)

绑定日志框架

slf4j可以绑定的日志框架:

注意点:

  1. 绑定时,只需要把相应的POM依赖导入即可,但是只能导入一种具体的日志框架实现
  2. 源码不动
绑定logback:
<!--slf4j core 使用slf4j必須添加-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.27</version>
</dependency>
<!--logback 日志实现-->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>


绑定NOP:
<!--slf4j core 使用slf4j必須添加-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.27</version>
</dependency>
<!--导入NOP日志实现-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-nop</artifactId>
    <version>1.7.25</version>
</dependency>


绑定log4j:需要导入适配器
<!--slf4j core 使用slf4j必須添加-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.27</version>
</dependency>

<!--导入log4j的适配器:log4j12-->
<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>
绑定JDK自带的日志实现:需要导入适配器即可
<!--slf4j core 使用slf4j必須添加-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.27</version>
</dependency>

<!--导入适配器-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-jdk14</artifactId>
    <version>1.7.25</version>
</dependency>

桥接旧的日志框架

背景:

  1. 一个项目刚开始时比较小,所以就使用JDK原生的日志框架;
  2. 但是随着项目逐渐扩大,原来的日志框架不满足现在庞大的项目,需要对日志框架进行升级,例如使用logback;
  3. 为了解决这种情况,SLF4J附带了几个桥接模块,这些模块将对log4j,JCL和java.util.logging API的调用重定向,就好像它们是对SLF4J API一样。

桥接过程示意图:

桥接操作步骤:

  1. 先去除之前老的日志框架的依赖
  2. 添加SLF4J提供的桥接组件
  3. 为项目添加SLF4J的具体实现

注意:桥接器(使slf4j关联不同)和适配器不能同时部署

总结:

  • slf4j的绑定:指定日志的实现框架
  • slf4j的桥接:换个新的日志实现
    1. 把老的日志实现从依赖中删除
    2.添加桥接器
    3.添加新的日志实现框架

日志实现

日志门面负责规范API,统一接口。真正实现日志记录的是具体的实现框架们。

日志框架出现的历史顺序:log4j -->JUL--> logback --> log4j2

JUL

Java util Logging:

  • 是java原生的日志框架,使用时不需要另外引用第三方类库
  • 相对其他日志框架使用方便,学习简单,能够在小型应用中灵活使用。

  1. Loggers:被称为记录器,应用程序通过获取Logger对象,调用其API来来发布日志信息。Logger通常时应用程序访问日志系统的入口程序。
  2. Appenders:也被称为Handlers,每个Logger都会关联一组Handlers,Logger会将日志交给关联Handlers处理,由Handlers负责将日志做记录。Handlers在此是一个抽象,其具体的实现决定了日志记录的位置可以是控制台、文件、网络上的其他日志服务或操作系统日志等。
  3. Layouts:也被称为Formatters,它负责对日志事件中的数据进行转换和格式化。Layouts决定了数据在一条日志记录中的最终形式。
  4. Level:每条日志消息都有一个关联的日志级别。该级别粗略指导了日志消息的重要性和紧迫,我可以将Level和Loggers,Appenders做关联以便于我们过滤消息。
  5. Filters:过滤器,根据Level确定哪些数据需要记录,哪些不需要。

工作过程:

  1. 用户使用Logger来进行日志记录,Logger持有若干个Handler,日志的输出操作是由Handler完成的。
  2. 在Handler在输出日志前,会经过Filter的过滤,判断哪些日志级别过滤放行哪些拦截,Handler会将日志内容输出到指定位置(日志文件、控制台等)。
  3. Handler在输出日志时会使用Layout,将输出内容进行排版。

第一个JUL实例

import java.util.logging.Level;
import java.util.logging.Logger;

public class Main {
    public static void main(String[] args) throws Exception {
        //1.获取日志对象记录器
        Logger logger = Logger.getLogger("kyle.Main"); //静态工厂构造器需要传入一个唯一标识,可以采用类全名
        //2.日志记录并输出(如果没有指定输出目标,则默认向控制台输出)
        logger.log(Level.INFO,"第一个日志记录输出");
        //logger.info("第一个日志记录输出"); 等价上一行代码,指定输出级别为INFO

        //通过占位符的形式输出
        String name = "kyle";
        Integer age = 22;
        logger.log(Level.INFO, "用户信息:{0},{1}", new Object[]{name,age});//数组
    }
}
/*
 * 控制台输出:
 * 1月 26, 2021 11:41:59 上午 kyle.Main main
 * 信息: 第一个日志记录输出
 * 1月 26, 2021 11:41:59 上午 kyle.Main main
 * 信息: 用户信息:kyle,22
 */

Logger之间的父子关系

这种父子关系通过树状结构存储,JUL在初始化时会创建一个顶层RootLogger作为所有Logger父Logger,存储上作为树状结构的根节点。

父子结构实例
import java.util.logging.Logger;

public class Main {
    public static void main(String[] args) throws Exception {
        //Logger的父子关系在代码中是根据静态构造器的传入包结构决定的
        Logger logger01 = Logger.getLogger("net.hackyle"); //父Logger
        Logger logger02 = Logger.getLogger("net.hackyle.package01"); //子Logger
        Logger logger03 = Logger.getLogger("net.hackyle.package02"); //子Logger

        //所有日志记录器对象的顶级父元素 class为java.util.logging.LogManager$RootLogger,name为空字符串
        System.out.println(logger01.getParent());

        System.out.println(logger02.getParent() == logger03.getParent()); //true
        System.out.println(logger02.getParent() == logger01); //true
    }
}

日志级别Level

日志级别由java.util.logging.Level类中的七个静态常量确定:

  • SEVERE(最高值):用于记录错误日志
  • WARNING:用于记录警告日志
  • INFO(默认级别):默认情况下,只会输出INFO以及更高级别的日志信息
  • CONFIG:用于记录配置日志
  • FINE:用于记录调试日志
  • FINER:
  • FINEST(最低值):

还有两个特殊的级别:

  • OFF,可用来关闭日志记录。
  • ALL,启用所有消息的日志记录。

输出不同的级别日志
import java.util.logging.Logger;

public class Main {
    public static void main(String[] args) throws Exception {
        //1.获取日志对象记录器
        Logger logger = Logger.getLogger("kyle.Main"); //静态工厂构造器需要传入一个唯一标识,可以采用类全名
        //2.输出不同级别的日志
        logger.severe("severe");
        logger.warning("warning");
        logger.info("info"); //默认情况下,只会输出INFO以及更高级别的日志信息
        logger.config("cofnig");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");
    }
}
/*
 * 控制台输出:
 * 1月 26, 2021 11:52:10 上午 kyle.Main main
 * 严重: severe
 * 1月 26, 2021 11:52:11 上午 kyle.Main main
 * 警告: warning
 * 1月 26, 2021 11:52:11 上午 kyle.Main main
 * 信息: info
 */

自定义日志输出级别
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

public class Main {
    public static void main(String[] args) throws Exception {
        //获取日志对象记录器
        Logger logger = Logger.getLogger("kyle.Main"); //静态工厂构造器需要传入一个唯一标识,可以采用类全名
        //关闭默认的日志级别
        logger.setUseParentHandlers(false);

        //Handler负责将日志做记录,因为是在我们是将日志输出到控制台,所以使用ConsoleHandler
        ConsoleHandler consoleHandler = new ConsoleHandler();
        SimpleFormatter simpleFormatter = new SimpleFormatter(); //格式转换
        
        consoleHandler.setFormatter(simpleFormatter);
        logger.addHandler(consoleHandler);
        
        //配置级别
        logger.setLevel(Level.ALL);
        consoleHandler.setLevel(Level.ALL);
        
        //输出到日志文件
        FileHandler fileHandler = new FileHandler("C:/users/kyle/desktop/jul.log");
        fileHandler.setFormatter(simpleFormatter);
        logger.addHandler(fileHandler); //控制台、文件都有日志输出

        //输出不同级别的日志
        logger.severe("severe");
        logger.warning("warning");
        logger.info("info"); //默认情况下,只会输出INFO以及更高级别的日志信息
        logger.config("cofnig");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");
    }
}
/*
 * 控制台输出:
 * 1月 26, 2021 12:01:20 下午 kyle.Main main
 * 严重: severe
 * 1月 26, 2021 12:01:20 下午 kyle.Main main
 * 警告: warning
 * 1月 26, 2021 12:01:20 下午 kyle.Main main
 * 信息: info
 * 1月 26, 2021 12:01:20 下午 kyle.Main main
 * 配置: cofnig
 * 1月 26, 2021 12:01:20 下午 kyle.Main main
 * 详细: fine
 * 1月 26, 2021 12:01:20 下午 kyle.Main main
 * 较详细: finer
 * 1月 26, 2021 12:01:20 下午 kyle.Main main
 * 非常详细: finest
 */

配置文件

默认日志配置文件:

  • 根据打断点,开启调试模式,可以得知JUL会加载一个默认的配置文件,其路径为\%JAVAHOME\%jre\lib\logging.properties(JDK1.8)
  • JDK11的路径为:\%JAVAHOME\%config\logging.properties
  • 我们可以根据默认的配置文件格式,修改出自定义的日志配置文件

建立resources目录,将其指定为“资源目录”;新建myLogging文件,格式上述的默认配置文件:

handlers= java.util.logging.ConsoleHandler

.level= ALL

java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.maxLocks = 100
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

按照配置文件进行日志记录:

import java.io.InputStream;
import java.util.logging.LogManager;
import java.util.logging.Logger;

public class Main {
    public static void main(String[] args) throws Exception {
        //读取日志配置文件
        InputStream in = Main.class.getClassLoader().getResourceAsStream("myLogging.properties");
        //获取日志管理器对象
        LogManager logManager = LogManager.getLogManager();
        //通过日志管理器加载配置文件
        logManager.readConfiguration(in);

        //进行日志记录
        Logger logger = Logger.getLogger("net.hackyle");
        logger.severe("severe");
        logger.warning("warning");
        logger.info("info");
        logger.config("config");
        logger.fine("fine");
        logger.finer("finer");
        logger.finest("finest");
    }
}

//输出:
1月 26, 2021 12:47:06 下午 kyle.Main main
严重: severe
1月 26, 2021 12:47:06 下午 kyle.Main main
警告: warning
1月 26, 2021 12:47:06 下午 kyle.Main main
信息: info
1月 26, 2021 12:47:06 下午 kyle.Main main
配置: config
1月 26, 2021 12:47:06 下午 kyle.Main main
详细: fine
1月 26, 2021 12:47:06 下午 kyle.Main main
较详细: finer
1月 26, 2021 12:47:06 下午 kyle.Main main
非常详细: finest

总结:

  • Logger及其子父级关系
  • 配置文件自定义输出格式、输出路径、日志记录级别

log4j

  • Log4j是Apache下的一款开源的日志框架
  • 通过在项目中使用 Log4J,我们可以控制日志信息输出到控制台、文件、甚至是数据库中。
  • 我们可以控制每一条日志的输出格式,通过定义日志的输出级别,可以更灵活的控制日志的输出过程。方便项目的调试。
  • 官方网站: http://logging.apache.org/log4j/1.2/

第一个log4j程序(版本2.14)

导包(缺一不可):
	log4j-1.2-api-2.14.0.jar
	log4j-api-2.14.0.jar
	log4j-core-2.14.0.jar

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;

public class Main {
    public static void main(String[] args) throws Exception {
        // 初始化配置信息,在入门案例中暂不使用配置文件
        BasicConfigurator.configure();

        // 获取日志记录器对象
        Logger logger = Logger.getLogger(Main.class); //注意包的位置

        logger.fatal("fatal"); // 严重错误,一般会造成系统崩溃并终止运行
        logger.error("error"); //错误信息,不会影响系统运行
        logger.warn("warn");   //警告信息,可能会发生问题
        logger.info("info");   //运行信息,数据连接、网络连接、IO 操作等等
        logger.debug("debug"); //默认:调试信息,一般在开发中使用,记录程序变量参数传递信息等等
        logger.trace("trace"); //追踪信息,记录程序所有的流程信息
    }
}
//输出:
//    14:49:27.021 [main] FATAL kyle.Main - fatal
//    14:49:27.031 [main] ERROR kyle.Main - error

第一个log4j程序(版本1.2.17)

新建Maven项目,导入依赖
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;

public class Main {
    public static void main(String[] args) {
        BasicConfigurator.configure();
        // 获取日志记录器对象
        Logger logger = Logger.getLogger(Main.class); //注意包的位置

        logger.fatal("fatal"); // 严重错误,一般会造成系统崩溃并终止运行
        logger.error("error"); //错误信息,不会影响系统运行
        logger.warn("warn");   //警告信息,可能会发生问题
        logger.info("info");   //运行信息,数据连接、网络连接、IO 操作等等
        logger.debug("debug"); //默认:调试信息,一般在开发中使用,记录程序变量参数传递信息等等
        logger.trace("trace"); //追踪信息,记录程序所有的流程信息
        //输出:
        //0 [main] FATAL net.hackyle.Main  - fatal
        //1 [main] ERROR net.hackyle.Main  - error
        //1 [main] WARN net.hackyle.Main  - warn
        //1 [main] INFO net.hackyle.Main  - info
        //1 [main] DEBUG net.hackyle.Main  - debug
    }
}

三大组件

  1. Loggers(记录器):日志类别和级别,控制那些信息是需要记录的。
  2. Appenders (输出源):日志要输出的地方,例如输出到C:\users\Admin\desktop\test.log。
  3. Layouts(布局):日志以何种形式(或格式)输出,例如输出为HTML格式。

Loggers(记录器)

记录器功能:

  • 负责收集不同级别和处理日志
  • 使用静态工厂构造器获取,Logger的名字是大小写敏感的,可以是类全名、类的字节码文件
  • 同JUL,其命名有继承机制:例如: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 类的别名。

日志级别:

  • fatal 指出每个严重的错误事件将会导致应用程序的退出。
  • error 指出虽然发生错误事件,但仍然不影响系统的继续运行。
  • warn 表明会出现潜在的错误情形。
  • info 一般和在粗粒度级别上,强调应用程序的运行全程。
  • debug 一般用于细粒度级别上,对调试应用程序非常有帮助。
  • trace 是程序追踪,可以用于输出程序运行中的变量,显示执行的流程。

还有两个特殊的级别:

  • OFF,可用来关闭日志记录。
  • ALL,启用所有消息的日志记录。

特性:

  • 只输出级别不低于设定级别的日志信息。例如Loggers级别设定为INFO,则INFO、WARN、ERROR和FATAL级别的日志信息都会输出,而级别比INFO低的DEBUG则不会输出。
     

输出源Appenders

  1. org.apache.log4j.ConsoleAppender(控制台)
  2. org.apache.log4j.FileAppender(文件)
  3. org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
  4. org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
  5. org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
  6. org.apache.log4j.JDBCAppender(将日志信息保存到数据库)

布局:

  1. org.apache.log4j.HTMLLayout 格式化日志输出为HTML表格形式
  2. org.apache.log4j.SimpleLayout 简单的日志输出格式化,打印的日志格式为(info - message)
  3. org.apache.log4j.PatternLayout 最强大的格式化期,可以根据自定义格式输出日志,如果没有指定转换格式,就是用默认的转换格式
  4. org.apache.log4j.TTCCLayout 包含日志产生的时间、线程、类别等信息

配置文件

Log4j支持两种配置文件格式:

  • 一种是XML格式的文件
  • 一种是properties属性文件

第一个Properities配置文件实例

新建Maven项目,导入依赖(版本1.2.17),在resources目录下建立一个log4j.properties:

# rootLogger:顶级父元素配置
# 指定日志级别,指定日志的输出位置,用逗号分隔
log4j.rootLogger = trace,console

# 指定朝输出控制台输出
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 指定日志的输出格式
log4j.appender.console.layout = org.apache.log4j.PatternLayout
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;

public class Main {
    public static void main(String[] args) {
        BasicConfigurator.configure();
        // 获取日志记录器对象
        Logger logger = Logger.getLogger(Main.class); //注意包的位置

        logger.fatal("fatal"); // 严重错误,一般会造成系统崩溃并终止运行
        logger.error("error"); //错误信息,不会影响系统运行
        logger.warn("warn");   //警告信息,可能会发生问题
        logger.info("info");   //运行信息,数据连接、网络连接、IO 操作等等
        logger.debug("debug"); //默认:调试信息,一般在开发中使用,记录程序变量参数传递信息等等
        logger.trace("trace"); //追踪信息,记录程序所有的流程信息
        //输出:
        //fatal
        //0 [main] FATAL net.hackyle.Main  - fatal
        //error
        //1 [main] ERROR net.hackyle.Main  - error
        //warn
        //1 [main] WARN net.hackyle.Main  - warn
        //info
        //1 [main] INFO net.hackyle.Main  - info
        //debug
        //1 [main] DEBUG net.hackyle.Main  - debug
        //trace
        //1 [main] TRACE net.hackyle.Main  - trace
    }
}

自定义Logger

log4j.properties

# rootLogger:顶级父元素配置
# 指定日志级别,指定日志的输出位置,用逗号分隔
# log#4j.rootLogger = trace,console

# 自定义Logger
# 格式:log4j.logger.自己定义名字 = 日志级别,输出位置
# 在继承过程中,日志级别会覆盖,输出位置会继承
# 在名字中,可以是使用点来区分父子关系:
#   log4j.logger.Aa = trace,console 是Bb,Cc的父,Aa的父是rootLogger
#   log4j.logger.Aa.Bb = trace,console 父是Aa
#   log4j.logger.Aa.Cc = trace,console  父是Aa
log4j.logger.Aa = info,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
# 指定日志文件保存路径
log4j.appender.file.file = C:/users/kyle/desktop/aa.log
# 指定日志文件的字符集
log4j.appender.file.encoding = UTF-8
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

public class Main {
    public static void main(String[] args) {
        // 获取日志记录器对象
        Logger logger = Logger.getLogger("Aa"); //这里需要传入在配置文件中自定义Logger的名字
        logger.fatal("fatal"); // 严重错误,一般会造成系统崩溃并终止运行
        logger.error("error"); //错误信息,不会影响系统运行
        logger.warn("warn");   //警告信息,可能会发生问题
        logger.info("info");   //运行信息,数据连接、网络连接、IO 操作等等
        logger.debug("debug"); //默认:调试信息,一般在开发中使用,记录程序变量参数传递信息等等
        logger.trace("trace"); //追踪信息,记录程序所有的流程信息
    }
}

执行结果:

logback

Logback是由log4j创始人设计的另一个开源日志组件,性能比log4j要好。

官方网站:https://logback.qos.ch/index.html

Logback主要分为三个模块:

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

logback组件

  • Logger:日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。
  • Appender:用于指定日志输出的目的地,目的地可以是控制台、文件、数据库等等。
  • Layout:负责把事件转换成字符串,格式化的日志信息的输出。在logback中Layout对象被封装在encoder中。

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

  1. logback.groovy
  2. logback-test.xml
  3. logback.xml

如果以上三种配置文件均不存在,则会采用默认配置。

官方提供的log4j.properties转换成logback.xml:https://logback.qos.ch/translator/

第一个logback程序

创建Maven项目,导入依赖:

<!--日志门面-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>
<!--日志的具体实现-->
<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 Main {
    //slf4j为日志门面,logback为日志实现
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger("net.hackyle");
        //打印日志信息
        logger.error("error");
        logger.warn("warn");
        logger.info("info");
        logger.debug("debug");
        logger.trace("trace");

        // 使用占位符输出日志信息
        String name = "jack";
        Integer age = 18;
        logger.info("用户:{},{}", name, age);

        // 将系统异常信息写入日志
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            // e.printStackTrace();
            logger.info("出现异常:", e);
        }
    }
}
//输出:
//1月 27, 2021 9:56:21 上午 net.hackyle.Main main
//严重: error
//1月 27, 2021 9:56:21 上午 net.hackyle.Main main
//警告: warn
//1月 27, 2021 9:56:21 上午 net.hackyle.Main main
//信息: info
//1月 27, 2021 9:56:21 上午 net.hackyle.Main main
//信息: 用户:jack,18
//1月 27, 2021 9:56:21 上午 net.hackyle.Main main
//信息: 出现异常:
//java.lang.ArithmeticException: / by zero
//at net.hackyle.Main.main(Main.java:23)

配置输出到控制台

新建Maven项目,导入依赖上文中的slf4j和logback依赖

在resources资源文件夹下新建logback.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--主要步骤:-->
<!--    1.配置日志输出格式-->
<!--    2.配置输出地-->
<!--    3.配置日志记录器-->

    <!--配置日志格式-->
    <!--name:为这个输出格式取个名字;value:输出的格式控制-->
    <!--后期调用:${outputPattern}-->
    <property name="outputPattern" 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换行
          -->

    <!--配置日志输出地-->
    <!--name:为这个输出地取个名字;class:具体的输出实现-->
    <appender name="outputConsole" class="ch.qos.logback.core.ConsoleAppender">
        <!--控制输出流对象 默认 System.out 改为 System.err-->
        <target>System.err</target>
        <!--日志消息格式配置-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${outputPattern}</pattern>
        </encoder>
    </appender>

    <!--配置日志记录器-->
    <!--使用根日志记录器,指定输出级别为ALL-->
    <root level="ALL">
        <appender-ref ref="outputConsole" />
    </root>

</configuration>

测试:

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

public class Main {
    //slf4j为日志门面,logback为日志实现
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger("net.hackyle");
        //打印日志信息
        logger.error("error");
        logger.warn("warn");
        logger.info("info");
        logger.debug("debug");
        logger.trace("trace");
    }
}
//输出:(检查是否为配置文件中指定的输出格式)
//[ERROR] 2021-01-27 10:26:37.171 net.hackyle main 11 [main] error
//[WARN ] 2021-01-27 10:26:37.178 net.hackyle main 12 [main] warn
//[INFO ] 2021-01-27 10:26:37.179 net.hackyle main 13 [main] info
//[DEBUG] 2021-01-27 10:26:37.181 net.hackyle main 14 [main] debug
//[TRACE] 2021-01-27 10:26:37.181 net.hackyle main 15 [main] trace

配置输出到控制台

新建Maven项目,导入依赖上文中的slf4j和logback依赖

在resources资源文件夹下新建logback.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--主要步骤:-->
<!--    1.配置日志输出格式-->
<!--    2.配置输出地-->
<!--    3.配置日志记录器-->

    <!--配置日志格式-->
    <!--name:为这个输出格式取个名字;value:输出的格式控制-->
    <!--后期调用:${outputPattern}-->
    <property name="outputPattern" 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换行
          -->

    <!--定义日志文件保存路径属性-->
    <property name="log_dir" value="C:/users/kyle/desktop/log/" />

    <!--配置日志输出地-->
    <!--name:为这个输出地取个名字;class:具体的输出实现-->
    <appender name="output2File" class="ch.qos.logback.core.FileAppender">
        <!--日志文件保存路径-->
        <file>${log_dir}/logback.log</file>
        <!--日志消息格式配置-->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${outputPattern}</pattern>
        </encoder>
    </appender>

    <!--配置日志记录器-->
    <!--使用根日志记录器,指定输出级别为ALL-->
    <root level="ALL">
        <appender-ref ref="output2File" />
        <!--如果即想要控制台输出,又想要文件输出,则在这里多配置即可-->
        <!--<appender-ref ref="output2Console" />-->
        <!--<appender-ref ref="output2Database" />-->
    </root>

</configuration>

测试方式同上一个。

配置输出到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">
            <!-- 具体的格式,类似于property标签中的输出属性 -->
            <pattern>%-5level%d{yyyy-MM-dd HH:mm:ss.SSS}%c%M%L%thread%m</pattern>
        </layout>
    </encoder>
</appender>

日志拆分&过滤器

logback.xml
<!--日志拆分和归档压缩-->
<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>${outputPattern}</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>

    <!--日志级别过滤器-->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <!--日志过滤规则:超过ERROR级别的日志才进行输出到日志文件-->
        <level>ERROR</level>
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
</appender>

异步日志

背景:

  1. 到目前为止,日志的记录都是在主线程中的,日志后的代码要等到日志记录操作完毕后才能继续往下执行。
  2. 如果要实现日志记录完全不占用主线程,而是自己新开一个线程去处理记录日志的操作,这就需要异步日志实现。

logback.xml实现异步日志

<!--配置日志输出地-->
<!--name:为这个输出地取个名字;class:具体的输出实现-->
<appender name="outputConsole" class="ch.qos.logback.core.ConsoleAppender">
    <!--控制输出流对象 默认 System.out 改为 System.err-->
    <target>System.err</target>
    <!--日志消息格式配置-->
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <pattern>${outputPattern}</pattern>
    </encoder>
</appender>
<!--异步日志-->
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
    <!--指定某个具体的 appender-->
    <appender-ref ref="outputConsole"/>
</appender>

自定义Logger

logback.xml
<!--自定义 looger 对象
    name:取个名字,这里通过包的形式也会产生父子的继承关系
        例如:net.hackyle.aa就继承与net.hackyle
    level:只要级别超过了的,才会输出到日志
    additivity:自定义logger对象是否继承rootLogger,false表明不继承
 -->
<logger name="net.hackyle" level="info" additivity="false">
    <!-- 输出地 -->
    <appender-ref ref="output2File"/>
</logger>

log4j2

  • Apache Log4j 2是对Log4j的升级版,参考了logback的一些优秀的设计,并且修复了一些问题,因此带来了一些重大的提升
  • 官网: https://logging.apache.org/log4j/2.x/
  • 目前市面上最主流的日志门面就是SLF4J,虽然Log4j2也是日志门面,因为它的日志实现功能非常强大,性能优越。所以大家一般还是将Log4j2看作是日志的实现,Slf4j + Log4j2应该是未来的大势所趋。

第一个log4j2程序

导入依赖:

<!-- Log4j2 门面API:目前主流不会直接使用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;

public class Main {
    //log4j2为日志门面,log4j2为日志实现
    public static void main(String[] args) {
        Logger logger = LogManager.getLogger(Main.class);
        logger.fatal("fatal");
        logger.error("error"); //默认级别
        logger.warn("warn");
        logger.info("info");
        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

//11:21:49.214 [main] FATAL net.hackyle.Main - fatal
//11:21:49.219 [main] ERROR net.hackyle.Main - error

slf4j和log4j2组合

导入依赖:

<!--slf4j日志门面-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
<!-- Log4j2 日志实现 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.11.1</version>
</dependency>

log4j2配置文件

由于log4j2是参考这logback来进行设计的,所以在配置文件上有相似的地方。

异步日志

在同步的日志中,日志的操作需要主线程去执行,日志后的业务逻辑需要等到日志操作完成后才会去执行。而在异步日志中,一旦需要日志操作,则新开辟一些线程去处理日志,主线程可以继续执行接下来的业务逻辑代码,不影响整体速度。

Log4j2最牛的地方在于异步输出日志时的性能表现

Log4j2提供了两种实现日志的方式,一个是通过AsyncAppender,一个是通过AsyncLogger(推荐使用),分别对应前面我们说的Appender组件和Logger组件。

配置异步日志需要添加依赖

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

AsyncAppender方式

log4j2.xml配置文件
<?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>

AsyncLogger方式

AsyncLogger才是log4j2 的重头戏,也是官方推荐的异步方式。它可以使得调用Logger.log返回的更快。你可以有两种选择:全局异步和混合异步。

  • 全局异步就是,所有的日志都异步的记录,在配置文件上不用做任何改动,只需要添加一个log4j2.component.properties 配置;

  • 混合异步就是,你可以在应用中同时使用同步日志和异步日志,这使得日志的配置方式更加灵活。但是注意:如果要使用混合异步,那就要把全局异步的关闭。
配置混合异步
<?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.itheima 日志是异步的,root日志是同步的
        <AsyncLogger name="com.itheima" level="trace" includeLocation="false" additivity="false">
            <AppenderRef ref="file"/>
        </AsyncLogger>

        <Root level="info" includeLocation="true">
            <AppenderRef ref="file"/>
        </Root>
    </Loggers>
</Configuration>

使用异步日志需要注意的问题:

  1. 如果使用异步日志,AsyncAppender、AsyncLogger和全局日志,不要同时出现。要么使用呢全局异步,要么使用混合异步。如果同时出现,性能会和AsyncAppender一致,降至最低。
  2. 设置includeLocation=false ,打印位置信息会急剧降低异步日志的性能,比同步日志还要慢。

总结:

  • 日志门面:
    • JCL(Jakarta Commons Logging):JDK内置,弱,基本不用
    • slf4j( Simple Logging Facade for Java):强,流行使用
  • 日志的实现:
    • JUL(java util logging):JDK内置,小项目中用
    • log4j:可以将日志记录到控制台、文件、数据库中,支持properties和xml的文件格式
    • logback:除了log4j的功能外,还支持将日志过滤、日志拆分、异步日志等
    • log4j2:拥有logback的全部功能,性能更强,尤其是异步日志。

  • slf4j+log4j2的组合方式是当下最流行的方式。

 

Java日志框架+日志门面详解,史上最全! 1996年早期,欧洲安全电子市场项目组决定编写它自己的程序跟踪API(Tracing API)。经过不断的完善,这个API终于成为一个十分受欢迎的Java日志软件包,即Log4j。后来Log4j成为Apache基金会项目中的一员。期间Log4j近乎成了Java社区的日志标准。据说Apache基金会还曾经建议sun引入Log4j到java的标准库中,但Sun拒绝了。2002年Java1.4发布,Sun推出了自己的日志库JUL(Java Util Logging),其实现基本模仿了Log4j的实现。 阅读详情

相关推荐

用C#语言实现记事本

本次程序设计作业为用C#语言设计一个记事本,设计的记事本应与电脑系统自带的记事本类似,最后实现的界面及功能应类似于下图。

qq_57738695的博客 2847

java slf4j日志级别_java - 在slf4j中设置运行时消息的日志级别 - 堆栈内存溢出

===============>>#1 票数:41 已采纳使用slf4j无法做到这slf4j 。我想,缺少这个功能的原因是,几乎不可能为slf4j构建一个Level类型,它可以有效地映射到Facade后面所有可能的日志记录实现中使用的Level (或等效)类型。 或者,设计人员认为您的用例太不寻常 ,无法证明支持它的开销。关于@ ripper234的用例 (单元测试),我认为实用的...

weixin_39702559的博客 1138

BALM2深度解析 | 港大MARS实验室如何用点簇革新激光BA?

本文深度解析了港大MARS实验室提出的BALM2方案如何通过点簇技术革新激光BA(Bundle Adjustment)处理。BALM2将点云数据聚合为点簇,显著提升计算效率,实测显示其计算耗时仅为传统方法的1/20。文章详细介绍了点簇的核心原理、二阶优化方法及与传统方法的性能对比,展示了其在激光SLAM领域的突破性应用。

weixin_29212867的博客 152

java日志框架JUL、JCL、Slf4j、Log4j、Log4j2、Logback 一网打尽

为什么程序需要记录日志 我们不可能实时的24小时对系统进行人工监控,那么如果程序出现异常错误时要如何排查呢?并且系统在运行时做了哪些事情我们又从何得知呢?这个时候日志这个概念就出现了,日志的出现对系统监控和异常分析起着至关重要的作用; 就拿java来说,在早期的日志都是通过System.out.println()进行记录的,但是这种方式不便于管理,所以apache最先开发了首个日志框架:log4j; 为日志框架奠定了基础; 日志框架出现的历史顺序为 : log4j → JUL → JCL → SLF4J →

叶新东老师的博客 5416

深入解析Unity3D中PostLateUpdate突然占用大量时间的原因

在Unity3D的生命周期中,是一个在LateUpdate之后被调用的方法。它通常用于处理一些需要在所有更新(包括物理更新和动画更新)之后进行的逻辑。由于它在游戏循环的后期阶段被调用,因此它通常用于处理与渲染直接相关的任务,如相机调整、UI更新等。突然占用大量时间的问题可能由多种原因引起,包括复杂的逻辑处理、频繁的资源加载、不恰当的渲染优化、插件或第三方库的副作用以及内存泄漏或垃圾回收等。为了解决这个问题,开发者需要仔细审查和优化中的代码,同时利用Unity提供的工具来监控和分析游戏性能。

Yori_22的博客 979

手把手教你搞定爱思唯尔LaTeX投稿:从模板到PDF生成的全流程避坑指南

本文详细介绍了使用LaTeX投稿爱思唯尔期刊的全流程避坑指南,包括模板选择、文件结构规范、投稿系统文件上传策略、PDF生成与检查等关键步骤。特别强调了避免常见错误如宏包冲突、图片丢失和参考文献问题,并提供了投稿后的应急方案和高阶自动化校验脚本,帮助研究者顺利完成投稿。

weixin_42525289的博客 507

快速了解常用日志技术(JCL、Slf4j、JUL、Log4j、Logback、Log4j2)

常见的日志门面 : JCL、slf4j 常见的日志实现: JUL、log4j、logback、log4j2

lydms的博客 1万+

万字详解YOLOv8网络结构Backbone/neck/head以及Conv、Bottleneck、C2f、SPPF、Detect等模块

YOLOv8是由Ultralytics开发的最先进的目标检测模型,推升了速度、准确性和用户友好性的界限。YOLO这一缩写代表“你只看一次”(You Only Look Once),通过在一次网络传递中同时预测所有边界框,提升了算法的效率和实时处理能力。相比之下,其他一些目标检测技术需要经过多个阶段或过程来完成检测。YOLOv8在流行的YOLOv5架构上进行了扩展,在多个方面提供了改进。

专注于图像领域,主要研究内容包括计算机视觉和深度学习,特别是在图像分类、目标检测和图像生成等方面有深入的研究和实践经验。 14万+

实现jul 日志重定向到 slf4j

需求背景 jul 指的是java.util.logging,是 java 内置的日志模块,目前流行的Java日志组件还包括 jcl(common-logging)、slf4j/log4j/logback 等等不同日志框架的定位和特性都存在差异,如 jcl、slf4j 提供的是日志门面(api)定义,log4j、logback则侧重于实现。 通常...

weixin_33739646的博客 452

基于STM32的智能浇花系统毕业设计(全部资料)

摘要:本文设计了一种基于STM32单片机的智能浇花系统,集成了环境温湿度检测、土壤温湿度监测、LCD显示、Wi-Fi联网及手机APP远程控制等功能。系统通过传感器实时采集环境参数,自动控制水泵工作实现智能灌溉,同时支持用户通过手机APP远程监控和调节。论文详细阐述了硬件选型、软件设计及系统测试过程,验证了系统的稳定性和实用性。该设计为家庭园艺提供了智能化解决方案,具有节水高效、操作便捷等特点。关键词:STM32;智能灌溉;远程控制;环境监测;Wi-Fi通信。

3254968421的博客 1194

GLM-4本地部署的实战教程

本文主要介绍了GLM-4-9B本地部署的实战教程,希望对学习和使用大模型的同学们有所帮助。 文章目录 1. 前言 2. 配置环境 2.1 安装虚拟环境 2.2 安装依赖库 2.3 下载模型文件 3. 运行glm-4-9b-chat

herosunly的博客 25万+

Jul日志转为Slf4j输出

java.util.logging.Logger打印出来的日志,如果要转为slf4j输出,要做以下两步: 在maven依赖里面加上jul-to-slf4j包(https://mvnrepository.com/artifact/org.slf4j/jul-to-slf4j) main函数里面添加下列语句: SLF4JBridgeHandler.removeHandlersForRootLogg...

随笔 6398

Python在振动信号处理中的应用(一):振动信号消除多项式趋势项

文章目录一、概述二、最小二乘法去除趋势项原理三、python模块介绍3.1 scikitlearn简介(1)转化器(Transformer)(2)估计器(Estimator)3.2 线性回归LinearRegression3.3 多项式PolynomialFeatures3.4 工作流Pipeline四、python代码实现4.1 构造模拟信号4.2 编写去除趋势项主代码4.3 展示算法效果五、TIPS 一、概述   在振动测试过程中,传感器或采集设备由于自身性能问题或受环境干扰(如温度、供电等影响),容易

白银时代的博客 8578

Java日志体系框架总结:JUL、JCL、SLF4J、Log4j、Logback、Log4j2

概述日志记录是应用程序运行中必不可少的一部分。具有良好格式和完备信息的日志,可以在程序出现问题时帮助开发人员迅速地定位错误的根源。日志所能提供的功能是多种多样的,包括记录程序运行时产生的错误信息、状态信息、调试信息和执行时间信息等。System.out.println、System.err.println及异常对象的pri...

cnzzs的博客 1159

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

java日志打印

日志框架(也叫日志实现)用来有效方便的记录日志信息。

我爱写代码 我爱写代码 我爱写代码 527

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Java日志 解读

Java日志的使用 以及SpringBoot日志与AOP的结合

ifspirt的博客 2222

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

java日志门面之JCL和SLF4J

什么时日志门面?随着系统开发的进行,可能会更新不同的日志框架,造成当前系统中存在不同的日志依赖,让我们难以统一的管理和控制。借鉴JDBC的思想,为日志系统也提供一套门面,那么我们就可以面向这些接口规范来开发,避免了直接依赖具体的日志框架。这样我们的系统在日志中,就存在了日志的门面和日志的实现。常见的日志框架及日志门面常见日志门面:JCL、slf4j常见日志实现:JUL、log4j、logback、log4j2框架诞生顺序:log4j --> JUL -->JCL-->slf4j。

从基础到源码解析的学习记录 9853

最详细、最全面的【Java日志框架】介绍,建议收藏,包含JUL、log4j、logback、log4j2等所有主流框架

本文为 【Java日志框架】 相关知识,对日志的概念与作用,JUL日志框架,Log4j日志框架,Logback日志框架,Log4j2日志框架,日志门面如Slf4j日志门面、JCL日志门面等进行详尽介绍~

小新要变强的博客 3919

日志框架Log4j、JUL、JCL、Slf4j、Logback、Log4j2

日志门面技术借鉴了 JDBC 的思想,为日志系统也提供了一套门面,开发者就可以面向这些接口规范来开发,避免了直接依赖具体日志框架,因为不同的日志框架有不同的用法,所以导致在更换日志框架时就需要改动大量的代码,所以为了减少开发量,就有了日志门面技术,门面技术简单地理解就是说你的日志框架变了,但是不需要改代码,只修改依赖和配置文件即可,提高了开发效率;目前主流的日志门面技术有2种JCLSLF4J。

CVPR收割机的博客 2055

Java日志框架——JUL、Log4j、JCL、Slf4j、Logback、Log4j2

1、Java日志框架有关问题 1.1 为什么要使用日志框架 因为软件开发发展到今天已经很复杂了,特别是服务端软件、涉及到知识内容问题太多,我们为了解决一下问题(1、日志输出的格式 2、日志输出的位置 3、日志的优化,异步日志,日志文件的归档和压缩 4、日志系统的维护也就是要求我们面向接口的开发) 在某些方面使用别人成熟的框架,站在别人的基础之上开发自己的核心部分代码,一般来说框架经过沉淀,展现在我们面前的一般...

qq_44015021的博客 1840

log4j jul jcl logback slf4j log4j2 直接先后顺序。隶属组织,作用

Logback以其与SLF4J的紧密集成和性能著称,而Log4j 2则以其丰富的功能集、灵活的配置和对异步日志记录的支持著称。作用: 类似于JCL,但提供更简单的API和更好的性能,是一个通用的日志接口,允许绑定到不同的日志实现如Logback、Log4j、JUL。由Ceki Gülcü开发,Log4j的原作者之一,Logback被设计为Log4j的继任者,旨在解决Log4j的一些局限性。作用: 改善了性能和可配置性,引入了异步日志记录、自动重新配置、更强的插件系统,以及更安全的API设计。

qq_27661293的博客 1349

JUL、JCL、Log4j、Slf4j各种日志框架的使用

java中存在各种各样的与日志有关的框架,如: JUL:Java Util Logging。 JCL:Jakarta Commons Logging,也可称为"Apache Commons Logging"。 Log4j Log4j2 Logback Slf4j JUL的使用 JUL是jdk自带的无需引入第三方的jar。 package com.morris.spring.log; import java.util.logging.Logger; public class J

morris 1986

日志框架梳理(Log4j,Reload4j,JUL,JCL,SLF4J,Logback,Log4j2)

在了解日志框架时总会列出一系列框架:Log4j,Reload4j,JUL,JCL,SLF4J,Logback,Log4j2,这么多框架让人感到混乱,该怎么选取、该怎么用。接下来,让我们逐步理清这些框架及之间的关系。

longool的博客 2634

SLF4J、JCL、JUL、log4j、logback的关系

SLF4J、JCL、JUL、log4j、logback的关系1. 名词解释1.1 JUL(java.util.logging)1.2 log4j1.3 logback1.4 JCL(Jakarta/Apache Commons logging)1.5 SLF4J1.6 小结2. 桥接遗留接口参考 1. 名词解释 1.1 JUL(java.util.logging) Java自带的日志系统。 1.2...

ttyy1112的专栏 1264
上一篇: [Junit]单元测试工具Junit教程
下一篇: 【Maven】几分钟了解Maven
ALGO阿狗
博客等级 码龄9年 64粉丝 149原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值