Skip to content

日志框架

日志框架有哪些呢?

Java日志框架主要分为两大类:日志门面(或称为日志抽象层)和日志实现。以下是Java中一些常见的日志框架:

日志门面

  1. SLF4J(Simple Logging Facade for Java):
    • SLF4J是一套简单的Java日志门面,本身没有日志实现,但它为各种日志框架提供了一个统一的接口。
    • 使用SLF4J可以方便地在项目中切换不同的日志实现,而不需要修改大量的代码。
  2. JCL(Jakarta Commons Logging):
    • JCL是Apache基金会所属的项目,是一套Java日志接口。
    • 它以前叫Jakarta Commons Logging,后更名为Commons Logging。
    • JCL已经停止更新,一般不建议在新项目中使用。

日志实现

  1. Log4j:
    • Log4j是一个基于Java的日志记录工具,由Ceki Gülcü首创,现在是Apache软件基金会的一个项目。
    • Log4j提供了灵活的日志记录功能,包括日志级别、日志格式、日志输出目的地等。
    • 需要注意的是,Log4j 1.x版本已经停止更新,并且存在安全漏洞,建议升级到Log4j 2。
  2. Log4j 2:
    • Log4j 2是Apache开发的一款Log4j的升级产品,具有更好的性能和更多的功能。
    • Log4j 2不兼容Log4j 1.x,但提供了类似的API和配置方式。
  3. Logback:
    • Logback是由Ceki Gülcü创建的另一个日志框架,它是SLF4J的一个实现。
    • Logback声称在某些关键操作上(如判定是否记录一条日志语句)的性能优于Log4j。
    • Logback还提供了全面的免费文档。
  4. JUL(Java Util Logging):
    • JUL是Java平台自带的日志框架,从Java 1.4开始提供。
    • JUL提供了基本的日志记录功能,包括日志级别、日志格式、日志输出目的地等。
    • JUL的使用相对简单,但在复杂的应用场景中可能不如其他第三方日志框架灵活。

其他日志框架

除了上述常见的日志框架外,还有一些其他的Java日志框架,如:

  • java.util.logging:这是Java标准库中的日志框架,与JUL实际上是同一个框架。
  • Log4j X(Log4j的某个实验性或非官方版本):这些版本可能提供了额外的功能或修复了已知的问题,但通常不建议在生产环境中使用。
  • 第三方日志框架:还有一些由社区或商业组织开发的日志框架,它们可能具有特定的功能或优化,但通常不如上述框架广泛使用。

选择日志框架的建议

在选择Java日志框架时,建议考虑以下因素:

  • 性能:选择具有高效性能的日志框架,特别是在高并发或大型应用程序中。
  • 灵活性:选择能够提供灵活配置和扩展性的日志框架。
  • 兼容性:确保所选的日志框架与项目中的其他库和框架兼容。
  • 社区支持:选择有活跃社区支持和持续更新的日志框架。
  • 安全性:确保所选的日志框架没有已知的安全漏洞,并关注其安全更新。

综上所述,Java中常用的日志框架包括SLF4J(日志门面)、Log4j 2、Logback和JUL(日志实现)。在选择日志框架时,应根据项目的具体需求和上述因素进行综合考虑。

日志框架的发展史

提示:

  • 发展史:log4j > JUL > JCL > slf4j > logback > log4j2
  • 日志的接口:Jakarta Commons Logging 和 SLF4J。

Java日志框架的发展史可以追溯到上世纪90年代末,以下是对其发展过程的详细梳理:

早期日志记录方式

  • 在jdk1.3之前,Java工程师通常使用原始的System.out.println()System.err.println()e.printStackTrace()来进行日志记录。这种方式简单但功能有限,无法满足复杂的日志需求。

Log4j的诞生与普及

  • 1999年:Ceki Gülcü创建了Log4j项目。Log4j是一种基于Java的日志记录工具,它提供了灵活的日志记录功能,如日志级别、日志格式、日志输出目的地等。
  • Apache基金会:Log4j后来成为Apache基金会的一个项目,并几乎成为了Java日志框架的实际标准。然而,Apache希望将Log4j引入jdk的提议被Sun公司拒绝。

JUL的推出

  • 2002年:随着Java 1.4的发布,Sun公司推出了自己的日志框架JUL(Java Util Logging)。JUL的实现基本模仿了Log4j,但提供了不同的配置和使用方式。

JCL的出现

  • 2002年:为了解耦日志接口与实现,Apache推出了JCL(Jakarta Commons Logging)。JCL定义了一套日志接口,具体实现可以由Log4j或JUL来完成。这种设计使得应用程序可以在不修改代码的情况下切换不同的日志实现。

SLF4J与Logback的创建

  • 2006年:由于与Apache基金会关于Commons-Logging制定的标准存在分歧,Ceki Gülcü离开了Apache,并先后创建了SLF4J和Logback两个项目。
    • SLF4J:是一个日志门面,只提供接口,可以支持Logback、JUL、Log4j等日志实现。
    • Logback:是SLF4J的一个具体实现,提供了丰富的功能和高效的性能。

Log4j 2的推出

  • 2012年:为了维护在Java日志领域的地位,防止JCL、Log4j被SLF4J、Logback组合取代,Apache推出了Log4j 2。Log4j 2与Log4j 1.x不兼容,但提供了更多的功能和更高的性能。

现代化日志框架的发展

  • 随着互联网和大数据的蓬勃发展,分布式日志系统以及日志分析系统得到了广泛应用。Java日志框架也在不断发展,以满足更复杂的日志需求。
  • 例如,Spring Boot等现代化Java框架开始内置对结构化日志的支持,使得日志信息能够以结构化的方式输出,便于后续的分析和处理。

综上所述,Java日志框架的发展史经历了从简单的日志记录方式到复杂的日志框架体系的过程。在这个过程中,不断有新的日志框架涌现,同时也有旧的框架被优化或替代。随着技术的不断进步和应用场景的不断扩展,Java日志框架将继续发展和完善。

Java Util Logging(JUL)

示例详细用法请参考https://gitee.com/dexterleslie/demonstration/tree/master/demo-java/demo-library/demo-logging-slf4j-parent中的demo-jul

什么是 Java Util Logging?

Java Util Logging(JUL,Java 实用日志记录框架)是 Java 平台自带的日志记录框架,它位于 java.util.logging 包中。JUL 提供了一种灵活的日志记录机制,允许开发人员记录应用程序的运行时信息,包括调试信息、错误信息、警告信息等。以下是对 Java Util Logging 的一些基本介绍和使用指南:

基本概念

  1. Logger:日志记录器,是记录日志信息的核心对象。每个 Logger 对象都有一个名称,通常与记录日志的类或子系统相对应。
  2. Handler:处理器,负责将日志记录输出到不同的目标,如控制台、文件等。
  3. Level:日志级别,用来控制日志信息的输出。Java Util Logging 提供了几个预定义的日志级别,如 SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST 和 ALL。
  4. Formatter:格式化器,用于定义日志信息的格式。
  5. LogRecord:日志记录对象,封装了日志信息,包括时间戳、日志级别、消息、异常等。

基本使用

  1. 获取 Logger 对象
java
import java.util.logging.Logger;
 
public class MyApp {
    private static final Logger logger = Logger.getLogger(MyApp.class.getName());
 
    public static void main(String[] args) {
        logger.info("This is an info message");
    }
}
  1. 配置 Handler
java
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
 
public class MyApp {
    private static final Logger logger = Logger.getLogger(MyApp.class.getName());
 
    public static void main(String[] args) throws IOException {
        FileHandler fileHandler = new FileHandler("app.log", true);
        fileHandler.setFormatter(new SimpleFormatter());
        logger.addHandler(fileHandler);
 
        logger.info("This info message will be logged to the file");
    }
}
  1. 设置日志级别
java
import java.util.logging.Level;
import java.util.logging.Logger;
 
public class MyApp {
    private static final Logger logger = Logger.getLogger(MyApp.class.getName());
 
    public static void main(String[] args) {
        logger.setLevel(Level.FINE); // 只记录 FINE 级别及以上(包括 FINE)的日志
 
        logger.severe("This is a severe message");
        logger.warning("This is a warning message");
        logger.info("This is an info message");
        logger.fine("This is a fine message"); // 这条会被记录
        logger.finer("This is a finer message"); // 这条不会被记录
    }
}
  1. 使用 Formatter
java
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
 
class MyFormatter extends Formatter {
    @Override
    public String format(LogRecord record) {
        Date date = new Date(record.getMillis());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String formattedDate = sdf.format(date);
        return String.format("[%s] [%s] %s%n", formattedDate, record.getLevel(), record.getMessage());
    }
}
 
public class MyApp {
    private static final Logger logger = Logger.getLogger(MyApp.class.getName());
 
    public static void main(String[] args) throws IOException {
        FileHandler fileHandler = new FileHandler("app.log", true);
        fileHandler.setFormatter(new MyFormatter());
        logger.addHandler(fileHandler);
 
        logger.info("This info message will be logged with custom format");
    }
}

注意事项

  • JUL 提供了基本的日志记录功能,但对于更复杂的需求,可能需要更高级的日志框架,如 Log4j、SLF4J 等。
  • JUL 的配置通常通过编程方式实现,虽然它也支持通过配置文件(如 logging.properties)进行配置,但这种方式相对不如其他框架的配置文件灵活。
  • 在多线程环境中使用 JUL 时,需要注意线程安全问题,虽然 JUL 本身已经考虑了线程安全。

Java Util Logging 是一个简单而强大的日志记录工具,对于小型或中等规模的应用程序来说,它已经足够满足需求。然而,对于更复杂的应用场景,可能需要考虑更强大的日志记录解决方案。

使用默认配置

参考示例的 JulTests 的 test1 方法

打印所有级别日志

参考示例的 JulTests 的 test2 方法

Logger 父子继承关系

提示:工作中几乎不使用 JUL 所以不作实验。

日志配置和配置的加载

提示:工作中几乎不使用 JUL 所以不作实验。

自定义 Logger

提示:工作中几乎不使用 JUL 所以不作实验。

日志过滤器

提示:工作中几乎不使用 JUL 所以不作实验。

Log4j 和 Log4j2

Log4j和Log4j2都是Apache提供的Java日志记录框架,它们之间存在一定的联系和区别。以下是对两者的详细分析:

一、历史背景与联系

  • Log4j是Apache的一个早期开源项目,用于Java应用程序的日志记录。
  • 随着技术的发展和需求的变化,Log4j的升级版Log4j2应运而生。2015年5月,Apache宣布Log4j 1.x停止更新,最新版截止到1.2.17。
  • Log4j2参考了其他优秀日志框架(如Logback)的设计,并修复了一些问题,因此带来了显著的性能提升和功能增强。

二、主要区别

  1. 性能
    • Log4j2在性能上有显著提升,尤其是在高并发环境下。它使用了异步日志记录机制,可以提供比传统同步日志记录更高的吞吐量和更低的延迟。
    • 相比之下,Log4j在高负载情况下的性能可能稍逊一筹。
  2. 配置
    • Log4j2的配置更加灵活,支持XML、JSON、YAML等多种格式。这使得开发人员可以根据项目需求进行更细致的配置和定制。
    • Log4j则主要使用properties文件进行配置,虽然简单但灵活性稍逊。
  3. 异常处理
    • 在Logback中,Appender中的异常不会被应用感知到。但在Log4j2中,提供了一些异常处理机制,使得日志记录过程中的异常能够得到更好的处理。
  4. 自动重载配置
    • Log4j2提供了自动刷新参数配置的功能,可以动态地修改日志的级别而不需要重启应用。这提高了应用的可用性和灵活性。
  5. 无垃圾机制
    • 在大部分情况下,Log4j2都可以使用其设计的一套无垃圾机制,避免频繁的日志收集导致的JVM GC(垃圾收集)开销。这有助于提升应用的性能和稳定性。
  6. 依赖管理
    • Log4j2使用了新的包名和API,与Log4j不兼容。因此,在升级时需要重新引入依赖并进行代码迁移。
  7. 兼容性
    • 尽管Log4j2引入了新的特性和改进,但它仍然向后兼容Log4j1.x。这意味着可以继续使用旧的配置文件和API(尽管不推荐),以便在升级过程中保持平滑过渡。

三、功能与应用

  • Log4j和Log4j2都提供了丰富的日志记录功能,包括多种输出目标(如控制台、文件、数据库等)、过滤器、路由等。
  • 开发人员可以根据项目需求选择合适的日志记录框架,并配置相应的参数和格式。
  • Log4j2由于其高性能和灵活性,被广泛用于各种Java应用程序和框架中,如Apache Tomcat、Apache Kafka等。

四、安全性

  • 值得注意的是,Log4j2在某些版本中存在远程代码执行漏洞(如2021年11月发现的Apache Log4j2远程代码执行漏洞)。这些漏洞可能导致攻击者通过构造恶意请求来触发远程代码执行,从而获得目标服务器权限。
  • 因此,在使用Log4j2时,开发人员需要关注其安全更新和漏洞修复情况,并及时升级到安全的版本。

综上所述,Log4j和Log4j2都是优秀的Java日志记录框架,但Log4j2在性能、配置灵活性、异常处理等方面提供了显著的提升和改进。在选择和使用时,开发人员需要根据项目需求和安全要求进行权衡和选择。

Log4j

示例详细用法请参考https://gitee.com/dexterleslie/demonstration/tree/master/demo-java/demo-library/demo-logging-slf4j-parent中的demo-log4j

Log4j是Apache的一个开源项目,它作为Java语言的日志记录框架,为Java应用程序提供了灵活的日志记录功能。以下是对Log4j的详细介绍:

一、基本功能

Log4j允许开发人员控制日志信息输送的目的地,这些目的地可以是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等。同时,Log4j还可以控制每一条日志的输出格式,并通过定义每一条日志信息的级别,更加细致地控制日志的生成过程。

二、核心组件

Log4j主要由三个核心组件组成:

  1. Loggers(日志记录器):
    • 负责收集和处理日志记录。
    • 实例的命名通常是类的全限定名。
    • Logger之间存在继承关系,如果一个Logger没有定义自己的日志级别,那么它将继承其父Logger的日志级别。
    • 有一个特殊的Logger叫做“root”,它是所有Logger的根,其他所有的Logger都会直接或者间接地继承自root。
  2. Appenders(输出控制器):
    • 指定日志的输出方式。
    • 常见的Appender有ConsoleAppender(将日志输出到控制台)、FileAppender(将日志输出到文件中)、DailyRollingFileAppender(每天输出到一个新的文件)等。
    • 一个Logger可以绑定多个Appender,即一条日志记录可以同时输出到不同的终端。
  3. Layout(日志格式化器):
    • 控制日志信息的输出格式。
    • 常见的Layout有HTMLLayout(将日志输出为HTML表格形式)、PatternLayout(可以灵活地指定布局模式)、SimpleLayout(简单的日志输出格式化)等。
    • PatternLayout是最强大的格式化组件,它允许开发人员自定义日志的输出格式。

三、配置方式

Log4j的配置可以通过一个配置文件来灵活地进行,而不需要修改应用的代码。Log4j支持两种配置文件方式:XML和properties(key=value)格式。配置文件主要用于配置Loggers、Appenders和Layout这三个组件。

四、日志级别

Log4j定义了多个日志级别,用于控制日志的输出。这些级别从低到高依次为:ALL、TRACE、DEBUG、INFO、WARN、ERROR、FATAL、OFF。其中,ALL级别表示打开所有级别的日志记录,而OFF级别则表示关闭所有的日志记录。开发人员可以根据项目需求选择合适的日志级别进行输出。

五、应用场景

Log4j广泛应用于各种Java应用程序和框架中,如Web应用、企业级应用、移动应用等。它提供了强大的日志记录功能,可以帮助开发人员更好地了解应用程序的运行情况,及时发现和解决问题。

六、安全性

尽管Log4j是一个强大的日志记录框架,但在使用过程中也需要注意其安全性。特别是在某些版本中存在的漏洞(如远程代码执行漏洞),可能会给应用程序带来安全风险。因此,开发人员需要关注Log4j的安全更新和漏洞修复情况,并及时升级到安全的版本。

综上所述,Log4j是一个功能强大、灵活易用的Java日志记录框架。它提供了丰富的日志记录功能、灵活的配置方式和多种日志级别选择,可以帮助开发人员更好地了解和管理应用程序的日志信息。在使用过程中,开发人员需要注意其安全性问题,并及时关注相关的安全更新和漏洞修复情况。

Appender 类型

Log4j 1.x 包含多种 Appender,用于指定日志信息的输出目的地。以下是 Log4j 1.x 中常见的 Appender:

  1. ConsoleAppender:将日志信息输出到控制台。这是开发和调试过程中常用的输出方式,因为它允许开发者在应用程序运行时实时查看日志信息。
  2. FileAppender:将日志信息输出到指定的文件中。这对于需要将日志信息持久化存储的情况非常有用。
  3. DailyRollingFileAppender:每天生成一个新的日志文件。这对于需要按天归档日志信息的情况非常有用,因为它可以自动将旧日志信息保存到之前的日志文件中,并开始写入新的日志文件。
  4. RollingFileAppender:当日志文件达到指定的大小时,生成一个新的日志文件。这对于需要限制日志文件大小的情况非常有用,因为它可以防止日志文件变得过大而难以管理。
  5. WriterAppender:将日志信息以流的形式发送到任何地方。这是一个灵活的 Appender,允许开发者将日志信息发送到任何实现了 Writer 接口的目标。

这些 Appender 提供了不同的日志输出选项,允许开发者根据应用程序的需求和日志管理的最佳实践来选择合适的输出方式。需要注意的是,在实际应用中,应根据具体的日志管理需求来选择适当的 Appender,并配置相应的参数以确保日志信息的正确输出和存储。

Layout 类型

Log4j 1.x 提供了多种 Layout,用于控制日志信息的输出格式。以下是 Log4j 1.x 中常见的 Layout:

  1. HTMLLayout:以 HTML 表格的形式输出日志信息。这对于需要将日志信息嵌入到网页中或者需要以网页形式查看日志信息的场景非常有用。
  2. PatternLayout:允许开发者自定义日志信息的输出格式。通过使用特定的模式字符串,PatternLayout 可以灵活地指定日志信息的布局,包括时间戳、线程名、日志级别、日志信息等内容。这使得 PatternLayout 成为 Log4j 1.x 中最常用的 Layout 之一。
  3. SimpleLayout:以一种简单的格式输出日志信息,通常只包括日志级别和日志信息本身。这种 Layout 的输出格式相对固定,适合于一些简单的日志记录需求。
  4. TTCCLayout:包含线程名、时间戳、日志级别、日志记录器(Logger)名称以及日志信息等内容。这种 Layout 提供了比 SimpleLayout 更丰富的信息,但比 PatternLayout 更加结构化。

这些 Layout 提供了不同的日志输出格式选项,允许开发者根据应用程序的需求和日志查看的便利性来选择合适的输出格式。需要注意的是,在实际应用中,应根据具体的日志查看需求和日志管理的最佳实践来选择合适的 Layout,并配置相应的参数以确保日志信息的正确输出和可读性。

Logger 继承关系

在Log4j 1.x中,Logger的继承关系是一个核心概念,它决定了日志记录的行为和方式。以下是对Log4j 1.x中Logger继承关系的详细阐述:

1. 根Logger(Root Logger)

  • Log4j 1.x中存在一个特殊的Logger,即root Logger。它是所有Logger的祖先或根节点。
  • 默认情况下,如果某个Logger没有被显式配置,它会继承root Logger的日志级别和Appender配置。

2. Logger的命名与继承

  • Logger的名字是大小写敏感的,并且通常与Java类的全限定名相对应。
  • Logger的继承关系是基于名字的。如果一个Logger(例如com.example.myapp.MyClass)没有被显式配置,Log4j会查找它的父Logger(例如com.example.myapp),如果还没找到,会继续向上查找,直到找到root Logger。

3. 日志级别的继承

  • 日志级别(如DEBUG、INFO、WARN、ERROR等)是可以被继承的。
  • 如果一个Logger没有显式设置日志级别,它会继承其父Logger的日志级别。
  • root Logger的日志级别可以在配置文件中设置,如log4j.rootLogger=INFO, console,这表示root Logger的日志级别为INFO,并且使用了一个名为console的Appender。

4. Appender的继承

  • Appender是Log4j中用于定义日志输出目的地的组件(如控制台、文件等)。
  • 默认情况下,一个Logger会继承其父Logger的所有Appender。
  • 但是,如果子Logger配置了additivity属性为false,那么它就不会继承父Logger的Appender。additivity属性的默认值为true。

5. 示例配置

以下是一个简单的log4j.properties配置文件示例,展示了Logger的继承关系:

properties
# 设置root Logger的日志级别为INFO,并指定一个控制台Appender
log4j.rootLogger=INFO, console

# 配置控制台Appender
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

# 为特定包设置不同的日志级别
log4j.logger.com.example.myapp=DEBUG

# 禁止特定类(或包)的日志信息继承到父Logger
# 注意:这里的设置会影响该类(或包)下的所有Logger
log4j.additivity.com.example.myapp.SomeClass=false

在这个配置文件中:

  • root Logger的日志级别被设置为INFO。
  • 一个名为console的Appender被配置为将日志消息输出到控制台。
  • com.example.myapp包下的Logger被设置为DEBUG级别,这意味着该包下的所有类都会输出DEBUG级别及以上(INFO、WARN、ERROR、FATAL)的日志消息。
  • 如果com.example.myapp.SomeClass类中有日志记录,并且additivity属性被设置为false,那么它的日志消息将不会输出到root Logger的Appender(即控制台),除非该类显式地配置了自己的Appender。

综上所述,Log4j 1.x中的Logger继承关系是基于名字的,并且涉及日志级别和Appender的继承。通过合理配置,可以实现灵活的日志记录和管理。

日志格式设置

  • %d:日期和时间。用于输出日志时间。可以使用ISO8601的日期时间格式,如%d{yyyy-MM-dd HH:mm:ss,SSS},也可以自定义其他格式。
  • %r:自应用程序启动以来的毫秒数
  • %p:日志级别(如 INFO、DEBUG、ERROR 等)
    • %-5p:输出日志级别,并占用5个字符的宽度,左对齐。如果日志级别的文本长度不足5个字符,剩余的位置会用空格填充。其中-表示左对齐,删除-%5p表示右对齐。
  • %c:类名。用于输出日志的类名。注意,在Log4j 1.x中,它输出的是日志信息所属的类的全名;而在某些情况下或特定版本中,也可能通过%C来输出类的简单名称(即不包含包名的类名)。例如:com.future.demo.Tests
    • %20.30c:输出类名,并占用20个字符的宽度,右对齐。如果超过30个字符,则会被截取。
  • %t:产生日志事件的线程的名称,例如:main
  • %x:NDC(Nested Diagnostic Context)。输出与当前线程关联的NDC信息。todo
  • %m:日志消息
  • %n:换行符。用于换行输出。
  • %l:日志发生的位置。表示输出日志信息的语句处于它所在的类的第几行。但请注意,这个转换符可能并不是在所有Log4j版本中都受支持。例如:com.future.demo.Tests.test1(Tests.java:12)
  • %F:文件名。在某些Log4j版本中,用于输出生成日志的类的文件名。但请注意,这个转换符的可用性可能因版本而异。例如:Tests.java
  • %L:代码行号。用于输出生成日志的代码行号。例如:1213
  • %M:方法名。在某些Log4j版本中,用于输出生成日志的方法名。但请注意,这个转换符的可用性可能因版本而异。例如:test1
  • %X:MDC(Mapped Diagnostic Context)。输出与当前线程关联的MDC信息。但请注意,MDC的支持可能需要在Log4j的配置中显式启用。todo
  • %%:百分号。用于输出一个百分号字符。

maven 依赖配置

xml
<!-- log4j 1.x 依赖 -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

通过 log4j.properties 配置

properties
# 根Logger设置,日志级别debug,appender名称(日志输出位置)stdout和D
log4j.rootLogger=debug,stdout,D

# stdout设置
# 日志输出到控制台
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# 调用ConsoleAppender的setLayout方法设置日志格式
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} %l %m%n

# D设置
# 日志输出到文件
log4j.appender.D=org.apache.log4j.FileAppender
# 调用FileAppender的setFile方法设置日志文件名称
log4j.appender.D.File=my.log
# 调用FileAppender的setThreshold方法设置日志阈值
log4j.appender.D.Threshold=INFO
log4j.appender.D.Append=true
log4j.appender.D.layout=org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} %l %m%n

设置指定包的日志级别

properties
# 设置com.future.demo包的日志级别
log4j.logger.com.future.demo=ERROR

additivity 用法

在Log4j 1.x中,additivity属性是一个重要的配置选项,它决定了子Logger是否继承父Logger的Appender(输出源)。以下是对additivity属性的详细解释:

一、additivity属性的功能

additivity属性的值可以是truefalse。它决定了子Logger是否会将日志信息发送到其父Logger的Appender中。

  • additivity设为true时(默认值),子Logger会继承父Logger的Appender,也就是说,子Logger的日志信息不仅会发送到自己的Appender中,还会发送到父Logger的Appender中。
  • additivity设为false时,子Logger不会继承父Logger的Appender,也就是说,子Logger的日志信息只会发送到自己的Appender中,而不会发送到父Logger的Appender中。

二、additivity属性的应用场景

additivity属性的作用在于控制日志信息的流向,以避免冗余的日志输出。以下是一些应用场景:

  • 当一个子模块的日志信息已经通过子模块的Logger输出,并且不希望再被父模块的Logger输出时,可以将子模块的Logger的additivity属性设置为false
  • 在某些情况下,可能需要对特定的Logger进行精细化的日志管理,例如,将某些日志信息输出到特定的文件或控制台中,而将其他日志信息输出到另一个文件或控制台中。此时,可以通过设置additivity属性来控制日志信息的流向。

三、additivity属性的配置方法

additivity属性可以通过Log4j的配置文件(如log4j.properties)进行设置。以下是一个示例配置:

在log4j.properties中配置

properties
# 设置root Logger的日志级别和Appender
log4j.rootLogger=INFO, stdout

# 配置stdout Appender
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

# 设置特定Logger的日志级别和Appender,并禁用additivity
log4j.logger.com.example.myapp=DEBUG, file
log4j.additivity.com.example.myapp=false

# 配置file Appender
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=/path/to/logfile.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

在这个配置中,com.example.myapp包下的Logger被设置为DEBUG级别,并且有一个名为file的Appender用于将日志信息输出到文件。由于additivity属性被设置为false,因此com.example.myapp包下的Logger的日志信息不会发送到root Logger的Appender(即控制台)中。

综上所述,additivity属性在Log4j 1.x中是一个重要的配置选项,它决定了子Logger是否继承父Logger的Appender。通过合理配置additivity属性,可以实现灵活的日志管理,避免冗余的日志输出。

本示例配置:

properties
# 根Logger设置,日志级别debug,appender名称(日志输出位置)stdout和D
log4j.rootLogger=debug,stdout,D

# stdout设置
# 日志输出到控制台
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# 调用ConsoleAppender的setLayout方法设置日志格式
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} %l %m%n

# D设置
# 日志输出到文件
log4j.appender.D=org.apache.log4j.FileAppender
# 调用FileAppender的setFile方法设置日志文件名称
log4j.appender.D.File=my.log
# 调用FileAppender的setThreshold方法设置日志阈值
log4j.appender.D.Threshold=INFO
log4j.appender.D.Append=true
log4j.appender.D.layout=org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} %l %m%n

# 下面配置用于测试additivity
# 当子Logger配置了自己的Appender后,设置additivity=false以阻止日志被发送父Logger的Appender重复输出
log4j.additivity.com.future.demo=false
log4j.logger.com.future.demo=INFO,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} self - %l %m%n

以上示例中 com.future.demo Logger 设置了自己的 Appender,配置 log4j.additivity.com.future.demo=false 以阻止日志被发送到父 Logger 的 Appender 重复输出。

Log4j2

示例详细用法请参考https://gitee.com/dexterleslie/demonstration/tree/master/demo-java/demo-library/demo-logging-slf4j-parent中的demo-log4j2

Log4j2是Apache软件基金会的一个日志框架,是Log4j的升级版本。相比于Log4j,Log4j2在性能、可靠性和灵活性方面都有显著的改进,被誉为是目前最优秀的Java日志框架。以下是对Log4j2的详细介绍:

一、主要特性

  1. 高性能:Log4j2使用异步日志记录机制,可以提供比传统同步日志记录更高的吞吐量和更低的延迟。在多线程场景中,异步记录器的吞吐量比Log4j 1.x和Logback高18倍。
  2. 灵活配置:Log4j2的配置文件使用XML、JSON或YAML等格式,允许将日志的格式、目标以及日志级别等属性进行灵活的配置和定制。
  3. 多种输出目标:Log4j2支持多种日志输出目标,包括控制台、文件、数据库、远程套接字、JMS和Apache Flume等。
  4. 强大的过滤器和路由:Log4j2支持过滤器功能,可以根据日志的级别、源、线程等条件进行过滤,在满足条件时决定是否记录日志。此外,还可以基于日志的特定属性进行路由,将不同的日志记录到不同的目标。
  5. 插件化:Log4j2使用插件架构,允许按需加载各种附加组件和扩展功能,如自定义输出目标、格式器、过滤器等。这使得Log4j2的功能可以根据需要进行扩展和定制。
  6. 自动重载配置:Log4j2可以在修改时自动重新加载其配置,无需重启应用即可动态修改日志级别等配置。
  7. 无垃圾机制:在稳态日志记录期间,Log4j2在独立应用程序中是无垃圾的,在Web应用程序中是低垃圾的。这减少了垃圾收集器的压力,并且可以提供更好的响应性能。

二、配置和使用

  1. 下载和导入:可以从Log4j的官方网站下载Log4j2的压缩文件,解压后将log4j-api和log4j-core两个jar包导入项目中。
  2. 配置文件:Log4j2的配置文件可以使用XML、JSON或YAML等格式。在配置文件中,可以定义日志的级别、输出目标、格式等。
  3. 使用日志框架:在Java代码中,可以使用Log4j2的API来记录日志。通常,会先获取一个Logger实例,然后使用该实例的方法来记录不同级别的日志信息。

三、应用场景

Log4j2被广泛用于各种Java应用程序和框架中,如Apache Tomcat(Java Web服务器)、Apache Kafka(分布式流处理平台)、Apache ActiveMQ(开源消息队列系统)等。它帮助开发人员更好地管理和分析应用程序的日志信息,从而进行问题追踪、状态监控和安全审计等工作。

四、安全漏洞

值得注意的是,Log4j2在过去也曾面临过安全漏洞的挑战。例如,2021年11月24日,阿里云安全团队向Apache官方报告了Apache Log4j2远程代码执行漏洞(Log4Shell)。该漏洞是由于Log4j2提供的lookup功能下的Jndi Lookup模块存在问题所导致的,允许攻击者通过构造恶意请求来触发远程代码执行漏洞,从而获得目标服务器权限。因此,在使用Log4j2时,需要关注其安全更新和补丁,以确保系统的安全性。

综上所述,Log4j2是一个功能强大且灵活的日志记录框架,提供了高性能、灵活配置、多种输出目标等特性。然而,在使用时也需要关注其安全漏洞等潜在问题,以确保系统的稳定性和安全性。

核心组件

Log4j2的核心组件主要包括以下几个:

  1. Logger:
    • Log4j2的核心组件,主要的日志记录接口。
    • Logger对象用于执行日志记录操作,如info()、debug()、error()等,这些方法用于记录不同级别的日志信息。
  2. LoggerContext:
    • 日志系统的上下文管理类,负责维护和管理所有的Logger及其相关配置。
    • LoggerContext是异步日志上下文实现,负责初始化Logger和配置,并管理Logger的生命周期。
  3. LoggerConfig:
    • 每个Logger都会分配一个相应的LoggerConfig。
    • LoggerConfig用于在Logger记录日志时找到相应的配置行为,如找到输出的Appender、输出的日志Layout、Pattern等。
  4. Appender:
    • 日志持久化组件,负责将Logger记录的每条LogEvent流向最终的持久化目标。
    • Appender有多种实现,如文件记录类型(FileAppender)、控制台输出类型(ConsoleAppender)等。
    • Appender还负责将日志信息按照指定的格式和策略输出到目标位置。
  5. Layout:
    • Appender在持久化日志时使用的最终输出格式。
    • Layout定义了日志信息的排版方式,如时间格式、线程名称等信息的展示方式。
    • 如果没有提供Layout,则使用默认的格式。
  6. Filter:
    • 用于对日志等级、特定Logger的LogEvent进行过滤和筛选。
    • Filter可以存在于多处,如LoggerConfig级别、Appender级别等。
    • 通过配置Filter,可以控制哪些日志信息被记录,哪些被忽略。

这些核心组件共同协作,使得Log4j2能够灵活、高效地记录和管理日志信息。在实际应用中,可以通过配置文件来定制这些组件的行为,以满足不同的日志记录需求。

Appender 类型

Log4j2 提供了多种 Appender 组件,用于将日志事件输出到不同的目标位置。以下是一些常用的 Appender 类型:

  1. ConsoleAppender:
    • 将日志输出到控制台(System.out 或 System.err)。
    • 通常用于开发和调试阶段,以便在控制台中实时查看日志信息。
  2. FileAppender:
    • 将日志输出到指定的文件中。
    • 每次运行程序时,如果文件已存在,则会覆盖该文件;如果希望追加到文件末尾,可以使用 append="true" 属性。
  3. RollingFileAppender:
    • 基于时间或文件大小等条件自动滚动日志文件。
    • 支持多种滚动策略,如基于时间的滚动(每天、每小时等)和基于文件大小的滚动(当文件达到一定大小时)。
    • 还可以配置归档文件的命名模式和压缩方式。
  4. RandomAccessFileAppender:
    • 类似于 FileAppender,但使用 RandomAccessFile 来写入日志。
    • 允许在文件中进行高效的随机访问,适用于需要频繁读取和写入日志文件的场景。
  5. JDBCAppender:
    • 将日志输出到数据库中。
    • 需要配置数据库连接信息和要插入日志的 SQL 语句。
  6. JMSAppender:
    • 将日志发送到 JMS 队列或主题中。
    • 适用于需要将日志信息发送到消息中间件进行处理的场景。
  7. SMTPAppender:
    • 当满足特定条件(如日志级别达到 ERROR 或 FATAL)时,通过电子邮件发送日志。
    • 需要配置 SMTP 服务器信息和邮件发送模板。
  8. SocketAppender:
    • 将日志发送到远程服务器上的 Socket 端口。
    • 适用于需要将日志信息发送到远程日志收集系统的场景。
  9. SyslogAppender:
    • 按照 RFC5424 标准将日志发送到远程 Syslog 服务器。
    • 适用于需要将日志信息发送到支持 Syslog 协议的系统中的场景。
  10. FlumeAppender:
    • 将日志发送到 Apache Flume 系统中。
    • Apache Flume 是一个分布式、可靠且可用的日志收集、聚合和传输系统。
  11. RewriteAppender:
    • 对日志事件进行重写或修改。
    • 可以用于在日志输出之前对日志信息进行动态修改或添加额外的信息。
  12. AsyncAppender:
    • 异步地将日志事件发送到其他 Appender。
    • 可以提高日志记录的性能,因为它允许日志事件在后台线程中异步地写入目标位置。

这些 Appender 组件可以根据实际需求进行选择和配置。在 Log4j2 的配置文件中,可以通过指定 Appender 的类型和相应的属性来配置它们。例如,在 XML 配置文件中,可以使用 <ConsoleAppender><FileAppender><RollingFileAppender> 等元素来配置不同类型的 Appender。

Layout 类型

Log4j2 提供了多种 Layout 组件,用于决定日志的输出格式。以下是一些常用的 Layout 类型:

  1. PatternLayout:
    • 使用模式字符串定义日志的格式。
    • 支持多种占位符,如 %d(日期和时间)、%t(线程名)、%p(日志级别)、%c(logger 的名称)、%m(日志消息)等。
    • 是最常用和灵活的 Layout。
  2. HTMLLayout:
    • 以 HTML 表格形式布局日志信息。
    • 适用于需要将日志输出到网页或 HTML 文件中的场景。
  3. SimpleLayout:
    • 简单的日志输出格式化。
    • 通常只包含日志级别和日志消息,如 INFO - message
    • 不常使用,因为缺乏足够的定制性。
  4. TTCCLayout:
    • 包含日志产生的时间、线程和类别等信息。
    • 提供了比 SimpleLayout 更多的上下文信息,但仍然不如 PatternLayout 灵活。
  5. JSONLayout:
    • 将日志输出为 JSON 格式。
    • 每个日志条目都是一个 JSON 对象,便于后续的处理和分析。
    • 适用于需要将日志发送到支持 JSON 解析的日志收集和分析系统的场景。
  6. XMLLayout:
    • 将日志输出为 XML 格式。
    • 每个日志条目都是一个 XML 元素,适用于需要将日志输出到支持 XML 解析的系统或文件中的场景。
  7. YamlLayout:
    • 将日志输出为 YAML 格式。
    • 类似于 JSONLayout,但使用了 YAML 语法。
  8. CsvLogLayout:
    • 将日志输出为 CSV 格式。
    • 适用于需要将日志数据导入到电子表格或进行数据分析的场景。
  9. SerializedLayout:
    • 将日志事件序列化为字节数组。
    • 适用于需要将日志事件发送到远程服务器进行处理的场景。

这些 Layout 组件可以根据实际需求进行选择和配置。在 Log4j2 的配置文件中,可以通过指定 Appender 的 Layout 属性来选择使用哪种 Layout。例如,在 XML 配置文件中,可以使用 <PatternLayout> 元素来配置 PatternLayout,如下所示:

xml
<Appender name="Console" class="org.apache.logging.log4j.core.appender.ConsoleAppender">
    <Layout class="org.apache.logging.log4j.core.layout.PatternLayout">
        <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Pattern>
    </Layout>
</Appender>

在这个例子中,<Pattern> 元素内的字符串定义了日志的输出格式,其中包含了日期、线程名、日志级别、logger 名称和日志消息等信息。

Logger 继承关系

注意:和 log4j 一致。

日志格式设置

  • %d:输出日志打印的时间,精确到毫秒。可以指定格式,如%d{yyyy-MM-dd HH:mm:ss.SSS}
  • %t:输出当前线程名称。
  • %-5level:输出日志级别,-5表示左对齐并且固定输出5个字符,不足在右边补空格。也可以使用%p,但不带宽度和对齐设置。
  • %logger{36}:输出logger名称,一般是类的全限定路径名,最长36个字符。也可以使用%c
  • %msg:输出要打印的业务日志信息。也可以使用%m
  • %n:换行符。
  • %C:输出Java类名(%F的简化形式,不包含文件路径)。
  • %F:输出日志所在的Java文件名。
  • %L:输出日志所在的行号。
  • %M:输出日志所在的方法名。
  • %l:输出日志位置,相当于%C.%M(%F.%L),即类名、方法名、文件名和行号的组合。
  • %hostName:输出本地机器名。
  • %hostAddress:输出本地IP地址。
  • %highlight:支持高亮显示日志级别,如%highlight{%-5level}
  • %x todo

maven 依赖配置

xml
<!-- log4j2依赖 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.24.2</version>
</dependency>

通过 log4j2.xml 配置

xml
<?xml version="1.0" encoding="utf-8" ?>
<!--
    status属性定义了Log4j2在初始化和运行过程中输出到控制台的日志信息的详细程度。这些日志信息主要用于调试和监控Log4j2的配置和运行状态。用于控制Log4j2日志框架本身的日志级别。
    monitorInterval属性设置为60秒,表示Log4j2将每60秒自动检测配置文件的更改并重新加载
-->
<Configuration status="error" monitorInterval="60">
    <!-- 定义appender -->
    <Appenders>
        <!-- 控制台appender设置 -->
        <Console name="stdout" target="SYSTEM_OUT">
            <!-- 控制台只输出info及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            <!-- 日志的格式 -->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %l{10} - %msg%n"/>
        </Console>

        <!-- 文件appender设置 -->
        <File name="file" fileName="my.log" append="true">
            <!-- 定义日志的格式 -->
            <PatternLayout>
                <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %l{10} - %msg%n</Pattern>
            </PatternLayout>
        </File>
    </Appenders>

    <!-- 定义logger,只有定义了logger并引入的appender,appender才会生效 -->
    <Loggers>
        <!-- 根root的logger -->
        <Root level="info">
            <!-- 引用上面定义的stdout appender -->
            <AppenderRef ref="stdout"/>
            <AppenderRef ref="file"/>
        </Root>
        <!-- 自定义的logger -->
        <Logger name="com.future.demo" level="info" additivity="false">
            <AppenderRef ref="stdout"/>
            <AppenderRef ref="file"/>
        </Logger>
    </Loggers>

</Configuration>

设置指定包的日志级别

xml
<!-- 定义logger,只有定义了logger并引入的appender,appender才会生效 -->
<Loggers>
    <!-- 根root的logger -->
    <Root level="info">
        <!-- 引用上面定义的stdout appender -->
        <AppenderRef ref="stdout"/>
        <AppenderRef ref="file"/>
    </Root>
    <!-- 自定义的logger -->
    <Logger name="com.future.demo" level="info" additivity="false">
        <AppenderRef ref="stdout"/>
        <AppenderRef ref="file"/>
    </Logger>
</Loggers>

additivity 用法

注意:和 log4j 用法一致。

Jakarta Commons Logging(JCL)

示例详细用法请参考https://gitee.com/dexterleslie/demonstration/tree/master/demo-java/demo-library/demo-logging-slf4j-parent中的demo-jcl

Jakarta Commons Logging(JCL)是Apache提供的一个通用日志API,以下是对其的详细介绍:

一、基本概述

JCL为“所有的Java日志实现”提供了一个统一的接口,它允许开发人员使用不同的具体日志实现工具,如Log4j、JDK自带的日志(JUL)等。同时,JCL自身也提供了一个简单的日志实现(SimpleLog),但功能相对较弱,因此一般不会单独使用。

二、核心组件

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

  • Log:是一个接口,其实现类提供了具体的日志记录功能。JCL支持多种日志实现,因此Log接口的实现类可以是对应日志框架的日志记录器。
  • LogFactory:是一个抽象类,用于创建Log实例。LogFactory的实现类会根据配置或类路径中的日志框架来决定使用哪个具体的日志实现。

三、配置与发现机制

  1. 配置文件:JCL能够在初始化期间自动查找配置文件进行配置。它首先会在classpath下寻找配置文件commons-logging.properties,并使用文件中org.apache.commons.logging.Log属性定义的Log实现类。如果找不到该配置文件,则会按照其他优先级进行查找。
  2. 系统环境变量:JCL还会查找系统环境变量org.apache.commons.logging.Log对应的Log实现类。
  3. 类路径查找:如果classpath中存在Log4j的包,JCL会自动使用Log4j作为日志实现类。如果不存在Log4j,则会使用JDK自身的日志实现类(JDK1.4以后才有日志实现类)。如果以上都不存在,则会使用JCL自己提供的SimpleLog作为日志实现。

四、使用方式

在使用JCL时,开发人员通常不需要直接实例化Log对象,而是通过LogFactory来获取Log实例。例如:

java
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
 
public class CommonsLoggingExample {
    private static final Log logger = LogFactory.getLog(CommonsLoggingExample.class);
 
    public static void main(String[] args) {
        logger.debug("This is a debug message");
        logger.info("This is an info message");
        logger.error("This is an error message");
    }
}

五、优点与局限性

  1. 优点
    • 提供了一个简单的日志接口,使得开发人员可以轻松地切换不同的日志实现。
    • 减少了与具体日志实现的耦合度,提高了代码的灵活性和可维护性。
  2. 局限性
    • 由于JCL的日志接口相对简单,因此可能无法充分利用具体日志实现的所有功能。
    • 在某些情况下,可能需要额外的配置才能正确地使用JCL。

六、与其他日志框架的集成

JCL可以与多种日志框架进行集成,包括Log4j、JUL、SLF4J等。通过与这些日志框架的集成,JCL可以更加灵活地满足开发人员的日志需求。

综上所述,Jakarta Commons Logging是一个简单而强大的日志API,它提供了统一的日志接口和多种日志实现的集成方式。虽然它有一些局限性,但在许多情况下仍然是一个不错的选择。

maven 依赖配置

xml
<!-- jcl依赖 -->
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>

配置使用 log4j 实现

提示:如果不引入 log4j,JCL 使用 JUL 作为其实现输出日志,如果引入 log4j JCL则使用 log4j。

xml
<!-- log4j 1.x 依赖 -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

log4j.properties 内容如下:

properties
# 根Logger设置,日志级别debug,appender名称(日志输出位置)stdout和D
log4j.rootLogger=debug,stdout,D

# stdout设置
# 日志输出到控制台
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# 调用ConsoleAppender的setLayout方法设置日志格式
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} %l %m%n

# D设置
# 日志输出到文件
log4j.appender.D=org.apache.log4j.FileAppender
# 调用FileAppender的setFile方法设置日志文件名称
log4j.appender.D.File=my.log
# 调用FileAppender的setThreshold方法设置日志阈值
log4j.appender.D.Threshold=INFO
log4j.appender.D.Append=true
log4j.appender.D.layout=org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} %l %m%n

# 设置com.future.demo包的日志级别
#log4j.logger.com.future.demo=INFO

# 下面配置用于测试additivity
# 当子Logger配置了自己的Appender后,设置additivity=false以阻止日志被发送父Logger的Appender重复输出
log4j.additivity.com.future.demo=false
log4j.logger.com.future.demo=INFO,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} self - %l %m%n

slf4j

SLF4J(Simple Logging Facade for Java)是一个为Java编程语言提供的简单日志门面,它定义了一套统一的日志API接口,使得开发者可以无缝切换具体的日志实现,如Log4j或Logback等。以下是对SLF4J的详细介绍:

一、SLF4J的主要作用

SLF4J的主要作用在于为应用程序提供一个统一的日志API,允许开发者在代码中使用统一的日志记录方法,而具体的日志实现则可以在部署时根据需要进行选择和替换。这种设计提高了代码的可移植性和灵活性,避免了在日志实现之间迁移的麻烦,简化了多模块项目中的日志管理。

二、SLF4J的核心组件

  1. Logger接口:用于记录日志消息,提供了一系列的日志级别方法,如debug()、info()、warn()、error()等。
  2. Marker接口:用于更精细的日志记录控制,可以附带特定标识符来标记日志消息。
  3. LoggerFactory接口:用于创建和管理Logger实例。
  4. 绑定实现:通过桥接库将SLF4J API绑定到具体的日志框架实现(如Logback、Log4j等)。

三、SLF4J的日志级别

SLF4J本身不提供日志级别的实现,而是依赖于绑定的日志框架来处理。常见的日志级别包括:

  1. DEBUG:详细的调试信息,通常用于开发和调试阶段。
  2. INFO:常规的系统信息,用于记录系统运行的流程。
  3. WARN:警告信息,表示可能出现问题,但系统仍能继续运行。
  4. ERROR:错误信息,表示已经发生了系统错误,需要关注。
  5. FATAL:致命错误,通常是严重错误,导致系统无法正常运行。

四、SLF4J的日志格式

标准化日志格式有助于快速解析和处理日志信息。常见的日志格式包括:

  1. 时间戳:提供事件发生的确切时间。
  2. 日志级别:表明消息的重要性。
  3. 日志来源:如类名、文件名或线程名。
  4. 消息内容:描述事件本身。
  5. 异常信息:如果适用,包含异常堆栈信息。

五、SLF4J的上下文信息

日志上下文信息是指与日志事件相关联的附加数据,如用户的会话ID、请求ID等。在SLF4J中,可以通过MDC(Mapped Diagnostic Context)和NDC(Nested Diagnostic Context)来管理上下文信息。MDC允许为每个线程存储键值对,而NDC则以调用栈的形式存储上下文信息。

六、SLF4J的绑定机制

SLF4J的绑定机制是指将SLF4J API与具体日志框架的实现进行链接的过程。这通常通过添加一个绑定库到项目的依赖中来完成。SLF4J官方提供了多个绑定实现,比如slf4j-simple、slf4j-log4j12、slf4j-jdk14等。

七、SLF4J在Spring项目中的集成

在Spring项目中集成SLF4J通常涉及以下步骤:

  1. 添加依赖:在项目的构建文件(如pom.xml或build.gradle)中添加SLF4J API和具体日志实现的依赖。
  2. 配置日志框架:根据选择的日志框架(如Logback或Log4j),配置相应的配置文件(如logback.xml或log4j2.xml),定义日志的级别、格式、输出目的地等。
  3. 使用Logger:在需要记录日志的类中,通过LoggerFactory获取Logger实例,并使用其提供的日志级别方法记录日志消息。

八、SLF4J的优势

  1. 灵活性:允许在运行时动态切换不同的日志后端,支持更高级的日志策略和配置。
  2. 兼容性:后端日志框架可能有所不同,但通过SLF4J可以统一API,减少学习成本和迁移成本。
  3. 可维护性:通过提供统一的日志API,使得日志代码更加简洁和易于维护。

综上所述,SLF4J是一个功能强大且灵活的日志门面,为Java开发者提供了统一的日志记录接口和丰富的日志管理功能。

slf4j + slf4j-simple

添加 maven 依赖

xml
<!-- slf4j + slf4j-simple 依赖 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>2.0.16</version>
</dependency>

测试

java
@Test
public void test1() {
    Logger logger = LoggerFactory.getLogger(Tests.class);

    logger.debug("Hello World");
    logger.info("Hello World");
}

slf4j-log4j12 和 log4j-slf4j-impl 区别

slf4j-log4j12和log4j-slf4j-impl都是Java日志框架中的适配器,用于将SLF4J的日志接口适配到具体的日志实现上。以下是两者的详细区别:

一、适配的日志实现版本

  • slf4j-log4j12
    • 专为Log4j 1.2版本设计的适配器。
    • 允许使用SLF4J API的应用程序通过slf4j-log4j12适配器来使用Log4j 1.2进行日志记录。
  • log4j-slf4j-impl
    • 是为Log4j 2或更高版本设计的适配器。
    • 允许使用SLF4J API的应用程序通过log4j-slf4j-impl适配器来使用Log4j 2进行日志记录。

二、依赖关系

  • slf4j-log4j12
    • 在项目中引入slf4j-log4j12时,通常还需要引入Log4j 1.2的jar包。
    • 配置相对简单,因为Log4j 1.2的API和配置方式相对固定。
  • log4j-slf4j-impl
    • 在项目中引入log4j-slf4j-impl时,需要同时引入Log4j 2的核心jar包(如log4j-core和log4j-api)。
    • 配置相对复杂,因为Log4j 2提供了更多的配置选项和特性。

三、使用场景

  • slf4j-log4j12
    • 适用于那些仍然在使用Log4j 1.2版本的项目。
    • 对于这些项目来说,升级到Log4j 2可能涉及到较大的改动和成本,因此slf4j-log4j12提供了一个便捷的日志记录方案。
  • log4j-slf4j-impl
    • 适用于那些已经升级到Log4j 2或计划升级到Log4j 2的项目。
    • 对于这些项目来说,log4j-slf4j-impl提供了与SLF4J的兼容性和Log4j 2的先进特性。

四、兼容性

  • slf4j-log4j12
    • 与Log4j 1.2版本兼容。
    • 不兼容Log4j 2或更高版本。
  • log4j-slf4j-impl
    • 与Log4j 2或更高版本兼容。
    • 不兼容Log4j 1.2版本(尽管可以通过其他方式桥接,但通常不推荐这样做)。

综上所述,slf4j-log4j12和log4j-slf4j-impl的主要区别在于它们适配的Log4j版本不同、依赖关系不同、使用场景不同以及兼容性不同。在选择适配器时,应根据项目的具体需求和日志实现版本来决定。

slf4j + log4j 1.x

添加 maven 依赖

xml
<!-- slf4j + log4j 1.x 依赖 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>2.0.16</version>
</dependency>

log4j.properties 配置如下:

properties
# 根Logger设置,日志级别debug,appender名称(日志输出位置)stdout
log4j.rootLogger=debug,stdout

# stdout设置
# 日志输出到控制台
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# 调用ConsoleAppender的setLayout方法设置日志格式
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} log4j-1.x %l %m%n

测试

java
@Test
public void test1() {
    Logger logger = LoggerFactory.getLogger(Tests.class);

    logger.debug("Hello World");
    logger.info("Hello World");
}

slf4j + log4j2

添加 maven 依赖

xml
<!-- slf4j + log4j2 依赖 -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.24.2</version>
</dependency>

log4j2.xml 配置如下:

xml
<?xml version="1.0" encoding="utf-8" ?>
<!--
    status属性定义了Log4j2在初始化和运行过程中输出到控制台的日志信息的详细程度。这些日志信息主要用于调试和监控Log4j2的配置和运行状态。用于控制Log4j2日志框架本身的日志级别。
    monitorInterval属性设置为60秒,表示Log4j2将每60秒自动检测配置文件的更改并重新加载
-->
<Configuration status="error" monitorInterval="60">
    <!-- 定义appender -->
    <Appenders>
        <!-- 控制台appender设置 -->
        <Console name="stdout" target="SYSTEM_OUT">
            <!-- 控制台只输出info及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            <!-- 日志的格式 -->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] log4j2 %l{10} - %msg%n"/>
        </Console>
    </Appenders>

    <!-- 定义logger,只有定义了logger并引入的appender,appender才会生效 -->
    <Loggers>
        <!-- 根root的logger -->
        <Root level="info">
            <!-- 引用上面定义的stdout appender -->
            <AppenderRef ref="stdout"/>
        </Root>
        <!-- 自定义的logger -->
        <Logger name="com.future.demo" level="info" additivity="false">
            <AppenderRef ref="stdout"/>
        </Logger>
    </Loggers>

</Configuration>

测试

java
@Test
public void test1() {
    Logger logger = LoggerFactory.getLogger(Tests.class);

    logger.debug("Hello World");
    logger.info("Hello World");
}

slf4j + jul

添加 maven 依赖

xml
<!-- slf4j + jul 依赖 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-jdk14</artifactId>
    <version>1.7.25</version>
</dependency>

测试

java
@Test
public void test1() {
    Logger logger = LoggerFactory.getLogger(Tests.class);

    logger.debug("Hello World");
    logger.info("Hello World");
}

slf4j + logback

提示:logback-classic 是 slf4j 一个具体实现,所以引入 logback-classic 即可使用 slf4j api 打印日志。

添加 maven 依赖

xml
<!-- slf4j + logback 依赖 -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.5.12</version>
</dependency>

logback.xml 配置如下:

xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
    scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
    scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
    debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="true" scanPeriod="60 seconds" debug="false">

    <!--
        上下文变量设置,用来定义变量值,其中name的值是变量的名称,value的值时变量定义的值。
        通过<property>定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。
    -->
    <property name="CONTEXT_NAME" value="logback-test"/>

    <!--
        上下文名称:<contextName>, 每个logger都关联到logger上下文,
        默认上下文名称为“default”。但可以使用<contextName>设置成其他名字,用于区分不同应用程序的记录。
        一旦设置,不能修改。
    -->
    <contextName>${CONTEXT_NAME}</contextName>

    <!-- 日志输出位置为控制台 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 对日志进行格式化。 -->
        <encoder>
            <pattern>
                %d{yyyy-MM-dd HH:mm:ss} [%5p] %t %class.%method\(%file:%line\) logback-classic - %m%n
            </pattern>
        </encoder>
    </appender>

    <!--
        特殊的<logger>元素,是根logger。只有一个level属性,应为已经被命名为"root".
        level:设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能设置为INHERITED或者同义词NULL。默认是DEBUG。
        <root>可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个logger。
    -->
    <root>
        <appender-ref ref="stdout"/>
    </root>
    <logger name="com.future.demo" additivity="false">
        <appender-ref ref="stdout"/>
    </logger>
</configuration>

测试

java
@Test
public void test1() {
    Logger logger = LoggerFactory.getLogger(Tests.class);

    logger.debug("Hello World");
    logger.info("Hello World");
}

MDC

MDC(Mapped Diagnostic Context)是SLF4J(Simple Logging Facade for Java)等日志框架提供的一种轻量级日志跟踪工具。以下是关于SLF4J MDC的详细解释:

一、MDC的定义与功能

MDC是一个线程安全的存放诊断日志的容器,它允许开发者将一些特定的数据(如用户ID、请求ID、会话ID等)存储到当前线程的上下文中。这些数据可以在日志消息中使用,从而实现对日志的跟踪和区分。在高并发环境中,由于多个请求可能同时处理,日志消息可能会交错在一起。使用MDC,可以为每个请求分配一个唯一的标识,并将该标识添加到每条日志消息中,从而方便地区分和跟踪每个请求的日志。

二、MDC的使用场景

MDC在日志跟踪中非常有用,特别是在多线程或高并发应用中。以下是一些常见的使用场景:

  1. 跟踪单个请求:在接收到请求时生成一个唯一的请求ID(trace-id),并在整个执行链路中带上此唯一ID。通过MDC,可以将这个ID添加到每条日志消息中,从而方便地跟踪单个请求的日志。
  2. 记录用户信息:在日志中记录请求用户的IP地址、用户ID等信息,以便在出现问题时能够快速定位到具体的用户。
  3. 日志过滤:通过MDC中的唯一标识,可以方便地过滤出特定请求的日志,从而进行问题排查和分析。

三、MDC的实现与配置

MDC本身不提供传递trace-id的能力,真正提供能力的是MDCAdapter接口的实现。不同的日志框架(如Log4j、Logback等)都有自己对应的MDCAdapter实现。在日志配置中,可以使用特定的占位符(如%X{trace-id})来获取并展示MDC中的数据。

以Logback为例,可以在logback.xml配置文件中使用<pattern>标签来定义日志的输出格式。在格式中,可以使用%X{key}来占位,其中key是MDC中存储数据的键名。当日志输出时,%X{key}会被替换为MDC中对应键名的值。

四、MDC的注意事项

  1. 线程安全:MDC是线程安全的,这意味着在多线程环境中,每个线程都可以有自己的MDC上下文,而不会相互干扰。
  2. 数据清理:在使用MDC时,需要注意在适当的时候清理数据。例如,在处理完一个请求后,应该从MDC中移除与该请求相关的数据,以避免数据泄露或占用不必要的内存。
  3. 性能考虑:虽然MDC提供了方便的日志跟踪功能,但在高并发环境中使用时,也需要注意性能问题。过多的日志输出和MDC操作可能会增加系统的开销,因此需要根据实际情况进行权衡和优化。

综上所述,MDC是SLF4J等日志框架提供的一种轻量级日志跟踪工具,它允许开发者将特定的数据存储到当前线程的上下文中,并在日志消息中使用这些数据。通过MDC,可以方便地跟踪和区分多线程或高并发应用中的单个请求日志。

使用

详细用法请参考https://gitee.com/dexterleslie/demonstration/tree/master/demo-java/demo-library/demo-logging-slf4j-parentdemo-spring-boot-logback示例

java代码中设置mdc

java
MDC.put("my-mdc1", UUID.randomUUID().toString());

logback-spring.xml配置文件中引用mdc

xml
%X{my-mdc1}

Logback

详细用法请参考https://gitee.com/dexterleslie/demonstration/tree/master/demo-java/demo-library/demo-logging-slf4j-parentdemo-spring-boot-logback示例

Logback是一个用于Java应用程序的日志框架,由log4j框架的创始人Ceki Gülcü开发,是log4j框架的继承者和改进版,提供了更好的性能、可扩展性和灵活性。以下是对Logback的详细介绍:

一、Logback的模块

Logback包括三个模块:

  • logback-core:提供了通用的日志记录功能。
  • logback-classic:提供了与SLF4J API和log4j API兼容的日志记录功能。
  • logback-access:提供了HTTP访问日志的功能。

二、Logback的基本概念

  1. Logger:Logger是Logback的核心概念,用于记录日志。Logger按照类的层次结构进行命名,每个Logger对象都有一个名字,如果没有显式指定名字,则使用当前类的全限定名作为它的名字。Logger有五个日志级别,分别是TRACE、DEBUG、INFO、WARN、ERROR,这五个级别优先级为TRACE<DEBUG<INFO<WARN<ERROR。在打印日志的时候,只会打印当前日志级别高于或者等于当前日志级别的日志信息。
  2. Appender:Appender用于定义日志的输出方式,例如输出到控制台、写入文件、发送邮件等。Logback有多种类型的Appender,可以同时输出到多个目标。Appender有两个必要属性:name和class。name属性指定appender名称,class属性指定appender的全限定名用于实例化。Appender的class类型主要有三种:ConsoleAppender、FileAppender、RollingFileAppender。
  3. Layout:Layout用于定义日志输出格式,例如日期格式、日志级别、线程名、类名等。在Logback中,layout对象被封装在encoder中,使用的encoder其实就是layout。encoder最主要的就是pattern标签,用于控制输出日志的格式。

三、Logback的配置

Logback的配置文件一般在项目的src/main/resources目录下创建,名为logback-spring.xml的文件。配置文件的主要标签包括:

  1. <configuration>:根标签,主要有三个属性debug、scan和scanPeriod。debug=true时会打印出logback内部的状态;scan=true表示开启logback在配置文件改变的时候自动去扫描的功能;scanPeriod属性可以指定扫描周期,默认情况下,一分钟扫描一次配置文件,看是否有更改。
  2. <logger>:对logger(日志记录器)进行配置,必须包含一个name属性,一个可选的level属性,一个可选additivity属性。name属性用于指定需要打印日志的包或类的全限定名;level取值可为TRACE、DEBUG、INFO、WARN、ERROR、ALL、OFF、INHERITED、NULL;additivity属性只是阻止了日志事件向上传播到父日志记录器,而不是改变日志级别的继承关系。logger标签可以包括0个或者多个<appender-ref>子标签,用于指定当前日志输出的目的地。
  3. <root>:通过root标签来进行配置root logger,它只支持一个属性level,不允许设置其它任何的属性。level属性的值可以为TRACE、DEBUG、INFO、WARN、ERROR、ALL、OFF。如果没有声明level属性,其默认值为DEBUG,但是他的值不能设置为INHERITED或NULL。root标签是一个特殊的logger标签,其主要作用是给其他的logger标签提供统一的配置(日志级别和日志输出目的地)。跟logger标签类似,root标签可以包含0或多个<appender-ref>子标签。
  4. <appender>:负责写日志的组件,有两个必要属性name和class。name属性指定appender名称,class属性指定appender的全限定名用于实例化。appender标签可以0或者多个<encoder><filter>子标签。
    • <encoder>:指定输出格式,通常使用PatternLayoutEncoder来指定输出格式。
    • <filter>:过滤器是appender的一个组件,用于判断appender是否输出日志的。一个appender可以包含多个过滤器。过滤器只能有三个值DENY、NEUTRAL、ACCEPT,其中ACCEPT是输出日志。可以有三个元素<level><onMatch><onMismatch>,分别用于设置过滤级别、配置符合过滤条件的操作、配置不符合过滤条件的操作。

四、Logback的用途

  1. 业务逻辑:在业务逻辑中,可以使用Logback记录与业务相关的信息、警告和错误。例如,在订单处理系统中,可以使用日志记录跟踪订单的处理状态、记录异常情况,以及记录用户操作等。这有助于排查问题、监视业务流程和追踪应用程序行为。
  2. 异常处理:在应用程序中的异常处理代码中,可以使用Logback记录异常详细信息,包括异常堆栈跟踪。这有助于开发人员了解异常原因以及如何修复它们。
  3. 性能监控:可以使用Logback记录应用程序的性能数据,如响应时间、请求处理时间等。这些信息有助于监视应用程序的性能,以及识别可能的性能问题。
  4. 安全审计:在涉及安全性的应用程序中,Logback可用于记录用户登录、权限更改和其他关键操作。这些日志可用于审计和合规性检查。
  5. 调试:在开发和测试阶段,Logback是调试代码的有力工具。可以在代码中插入调试信息,以便在需要时启用它们,并在调试时查看日志输出。

五、Logback的特性

  1. 异步日志:Logback支持异步日志记录,可以提高日志记录的性能。
  2. 动态配置:Logback支持动态配置,可以在运行时修改日志配置,而无需重启应用程序。
  3. 可扩展性:Logback提供了良好的可扩展性,可以通过自定义Appender、Layout和Filter等组件来满足特定的日志需求。

综上所述,Logback是一个功能强大、性能卓越的Java日志框架,适用于各种规模的应用程序。通过合理配置和使用Logback,可以有效地记录和管理应用程序的日志信息,提高开发效率和运维水平。

日志格式设置

通过 xml 配置

logback.xml 和 logback-spring.xml 配置区别

logback.xml和logback-spring.xml都是Logback日志框架的配置文件,用于配置Java应用的日志输出。以下是两者的详细对比:

一、logback.xml

  1. 定义:logback.xml是标准的Logback配置文件。
  2. 特性
    • 缺少Spring Boot特有的集成支持功能,不能直接读取Spring配置中的属性。
    • 不支持Spring的${}占位符,因此无法直接读取Spring配置文件(如application.yml或application.properties)中的属性,需要手动设置日志路径等参数。
    • 不支持<springProfile>标签,因此无法实现基于Profile的日志配置切换。
    • 支持热加载,但主要依赖Logback自身的配置文件监控,Spring Boot的DevTools热加载不会生效。
    • 在Spring Boot项目中,如果项目中没有logback-spring.xml,Spring Boot才会加载logback.xml。

二、logback-spring.xml

  1. 定义:logback-spring.xml是专门为Spring Boot提供的日志配置文件。
  2. 特性
    • 允许使用Spring Boot特有的功能,比如Spring配置的属性注入、基于Profile的配置切换等。
    • 支持通过Spring的${}占位符引入application.yml或application.properties中定义的属性,例如日志文件的存储路径。
    • 支持使用<springProfile>标签,根据不同的Spring Profile(如dev、prod)动态加载不同的日志配置,方便管理多环境日志。
    • 支持Spring Boot DevTools的热加载,当修改logback-spring.xml文件时,Spring Boot可以自动重启并应用新的日志配置。
    • 在Spring Boot启动时优先加载。如果项目中存在logback-spring.xml,Spring Boot会优先使用它作为日志配置文件。

三、使用场景

  1. logback-spring.xml
    • 需要读取Spring配置,如从application.yml或application.properties文件中读取日志配置(如日志文件路径或日志级别)。
    • 需要按环境配置日志,使用<springProfile>标签可以让不同环境(开发、测试、生产)加载不同的日志配置,适合多环境部署的项目。
    • 需要自动重启和热加载,在开发中logback-spring.xml可以支持Spring Boot DevTools的热加载功能,修改后立即生效,提高开发效率。
  2. logback.xml
    • 项目不是基于Spring Boot或者希望使用标准的Logback配置文件。
    • 日志配置简单,没有多环境需求,或不需要从Spring配置中读取属性。

综上所述,logback.xml和logback-spring.xml在功能和使用场景上存在差异。在Spring Boot项目中,如果需要利用Spring Boot的特有功能进行日志配置,推荐使用logback-spring.xml;如果项目不是基于Spring Boot或者希望使用标准的Logback配置文件,则可以选择logback.xml。

通过 include 标签包含其他配置文件

https://logback.qos.ch/manual/configuration.html#fileInclusion

被包含的 included.xml 文件内容如下:

xml
<?xml version="1.0" encoding="UTF-8" ?>
<included>
    <property name="my-property" value="My hello world!"/>
</included>

logback.xml 或者 logback-spring.xml 配置文件中 include 其他配置文件

xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <!-- 测试 include 标签 -->
    <include resource="included.xml"/>
    
    ...
</configuration>

使用property标签读取指定的properties配置(非application.properties配置)并注入到logback上下文中

application-dev.properties 内容如下:

properties
myk2=devk2

application-prod.properties 内容如下:

properties
myk2=prodk2

logback.xml 配置中读取 properties 配置

xml
<configuration debug="false">
    <springProfile name="!prod">
        <property resource="application-dev.properties"/>
    </springProfile>
    <springProfile name="prod">
        <property resource="application-prod.properties"/>
    </springProfile>
</configuration>
  • <springProfile name="!prod">用于判断spring profile

在 logback 配置中使用 Spring 配置

xml
<!-- 通过以下变量引用 Spring 配置 -->
${myk2}

使用springProperty注入Spring配置到logback上下文中

xml
<configuration debug="false">
	<springProperty name="needConsole" source="spring.application.console"/>
</configuration>
  • 读取application.propertiesspring.application.console配置并注入到logback上线文中的needConsole property

在 logback 配置中使用 needConsole property

xml
${needConsole}

if中引用logback上下文中的property

xml
<!-- 引用案例1 -->
<if condition='!property("spring.elk.logstash.tcp.url").equals("")'>
    <then>
        ...
    </then>
</if>

<!-- 引用案例2 -->
<if condition='!property("spring.application.production").equals("true")'>
    <then>
        <appender-ref ref="console"/>
    </then>
</if>

<!-- 引用案例3 -->
<if condition='!property("spring.elk.logstash.tcp.url").equals("")'>
    <then>
        <appender name="logstash" class="net.logstash.logback.appender.LogstashAccessTcpSocketAppender">
            <destination>${spring.elk.logstash.tcp.url}</destination>
            ...
        </appender>
    </then>
</if>

输出JSON格式日志

maven加入依赖用于输出JSON格式日志

xml
<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>6.6</version>
</dependency>
<dependency>
    <groupId>org.codehaus.janino</groupId>
    <artifactId>janino</artifactId>
</dependency>

JSON appenderLogger配置如下:

xml
<!-- 测试json encoder -->
<appender name="jsonAppender" class="ch.qos.logback.core.ConsoleAppender">
    <encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder">
        <customFields>{"module": "my-module"}</customFields>
    </encoder>
</appender>

<!-- 测试json encoder -->
<logger name="jsonLogger" level="debug" additivity="false">
    <appender-ref ref="jsonAppender"/>
</logger>

java代码中使用JSON logger

java
final static Logger jsonLogger = LoggerFactory.getLogger("jsonLogger");

// json encoder测试
jsonLogger.debug("测试json encoder");

格式修饰器

Logback 的格式修饰器(Format modifiers)在日志输出格式化中起到了关键作用,它们允许开发者调整日志字段的宽度、对齐方式以及处理超长字符的方式。以下是对 Logback 格式修饰器的详细解释:

一、格式修饰器的基本结构

格式修饰器通常放置在百分号(%)和转换字符之间。它们可以包括以下几个部分:

  1. 左对齐标志:用减号(-)表示,用于指定字段应该左对齐。如果没有这个标志,字段默认是右对齐的。
  2. 最小字段宽度修饰符:一个十进制常数,表示输出的最少字符数。如果数据项字符较少,将向左或向右填补空格直到达到最小宽度。具体填补方向取决于左对齐标志是否存在。
  3. (可选的)最大字段宽度修饰符:这个修饰符在 Logback 的标准 PatternLayout 中并不直接支持,但可以通过其他方式(如自定义 Layout)实现。

二、格式修饰器的使用示例

  1. 左对齐
    • %-5level:表示日志级别字段应该左对齐,并且最小宽度为 5 个字符。如果日志级别是 INFO,那么输出将是 INFO (注意末尾有一个空格)。
  2. 右对齐(默认)
    • %5level:表示日志级别字段应该右对齐,并且最小宽度为 5 个字符。如果日志级别是 INFO,那么输出将是 INFO(注意前面有一个空格)。
  3. 指定最小字段宽度
    • %logger{36}:表示 Logger 名称字段的最小宽度为 36 个字符。如果 Logger 名称超过 36 个字符,它将被截断。

三、注意事项

  1. 字段扩大:如果数据项大于最小字段宽度,字段会扩大以容纳数据。这意味着,即使设置了最小宽度,字段也不会被截断(除非使用了像 %logger{n} 这样的截断机制)。
  2. 字符处理:对于超长字符的处理,Logback 的格式修饰器主要通过截断来实现。例如,%logger{36} 会将 Logger 名称截断为最多 36 个字符。
  3. 组合使用:格式修饰器可以组合使用。例如,%-5.10level(尽管 .10 在标准 PatternLayout 中并不直接有效)可以表示一个左对齐的、最小宽度为 5 个字符的字段,但 .10 部分实际上在 Logback 的标准实现中不会起作用。然而,你可以通过自定义 Layout 来实现类似的效果。

四、实际应用

在 Logback 的配置文件(如 logback.xml)中,你可以使用这些格式修饰器来定义日志的输出格式。例如:

xml
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

在这个例子中,%-5level%logger{36} 就是格式修饰器的使用示例。它们分别指定了日志级别字段应该左对齐且最小宽度为 5 个字符,以及 Logger 名称字段的最小宽度为 36 个字符。

总的来说,Logback 的格式修饰器为开发者提供了灵活的方式来定制日志的输出格式,从而满足不同的日志记录需求。

SpringBoot 项目中使用 Logback

配置 Logback

不需要专门引入logback依赖,因为org.springframework.boot:spring-boot-starter-web依赖已经自动引入logback相关依赖

创建名为 logback-spring.xml 的 Logback 配置文件

在应用中使用logback

java
package com.future.demo;

import com.future.demo.package1.Tester1;
import com.future.demo.package2.Tester2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class ApplicationTests {
    final static Logger logger = LoggerFactory.getLogger(Application.class);

    @Test
    public void test() throws Exception {
        logger.debug("test method is called");
    }

}

使用 SpringBoot 默认控制台日志输出格式

https://stackoverflow.com/questions/30571319/spring-boot-logging-pattern

xml
<!--
    使用 SpringBoot 默认控制台日志输出格式
    https://stackoverflow.com/questions/30571319/spring-boot-logging-pattern
-->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<appender name="consoleAppenderDefault" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        <charset>utf8</charset>
    </encoder>
</appender>

设置根(root) Logger 日志级别

application.properties 配置如下:

properties
# 设置根(root)Logger 日志级别为 info
logging.level.root=info

其他

todo

变量定义和条件判断
https://dennis-xlc.gitbooks.io/the-logback-manual/content/en/chapter-3-configuration/configuration-file-syntax/variable-substitution.html
https://blog.csdn.net/wushengjun753/article/details/109510794

&& (logical and) and || (logical or) operators in Logback configuration (if statement)
https://stackoverflow.com/questions/41939861/logical-and-and-logical-or-operators-in-logback-configuration-if-stat

property、springProperty使用
https://blog.csdn.net/qq_34359363/article/details/104749341
1.该 <springProperty> 标签允许我们从Spring中显示属性,Environment 以便在Logback中使用。如果你想将 application.properties在回读配置中访问文件中的值,这将非常有用
2.标签的工作方式与Logback的标准 <property> 标签类似,但不是直接value 指定source属性(从Environment)指定。scope 如果需要将属性存储在local范围之外的其他位置,则可以使用该属性。如果您需要一个后备值,以防该属性未设置,则Environment可以使用该defaultValue属性。