Java 日志

目录

    • 日志门面与实现框架
      • 日志门面
      • 日志实现框架
      • 总结
    • SLF4j + Logback 的实现
      • 第一步:添加配置文件
        • logback.xml
      • LogTool.java 工具类(非必要)
      • 依赖导入
        • Java 程序
        • SpringBoot 项目
      • 测试
    • 其他的日志门面与实现框架

  • 日志门面:提供统一的日志输出接口

  • 日志实现:具体实现日志输出的代码

日志门面与实现框架

  • 使用 日志门面+日志实现框架 的方式,是为了:低耦合,日志的实现与业务代码通过 日志门面连接,在后续修改日志实现时,无需更改业务代码。

  • 这是 门面设计模式(外观设计模式)的典型应用。

日志门面

  • SLF4j(Simple Logging Facade For Java):一个为 Java 程序提供的统一日志输出接口,就是一个接口,

  • JCL(Jaka Commons Logging, Apache Commons Logging):Apache 提供的一个日志门面,提供统一的对外接口。

日志实现框架

  • JUL(Java util Logging)Java 原生的日志框架,使用时不需要引用第三方类库,使用方便

    • 7 个日志级别(从高到低):SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST

    • 同时还有 OFF、ALL 两个特别的日志级别,用来 关闭/打开 所有的日志

  • log4jApache 的一个开源项目

    • 7 个日志级别(从高到低):OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL
    日志级别 日志介绍
    OFF 最高日志级别,关闭所有日志
    FATAL 将会导致引用程序退出的错误
    ERROR 发生错误事件,但仍不影响系统的继续运行
    WARN 警告,存在潜在的错误
    INFO 一般用在粗粒度级别上,强调应用程序的运行全程
    DEBUG 一般用在细粒度级别上,用于调试应用程序
    ALL 最低日志级别,打开所有日志
  • log4j2log4j 的升级版,参考了 logback 的设计,同时进行了问题修复

    • 异常优化:提供了一些异常处理机制,来解决在 logback 中,应用无法感知到 Appener 异常。

    • 性能提升:相较于 log4j 和 logback,性能都有明显的提升。

    • 自动重载配置:参考 logback 的参数修改自动更新机制,提供自动刷新参数的设置。

    • 无垃圾机制:可以使用其设计的一套无垃圾机制(对象重用、内存缓冲),避免频繁的日志记录导致 JVM gc 压力过大。

  • logbackSpringBoot 默认的日志框架

    • 由三个模块组成:

      • logback-core:logback 核心包,开发人员可以以次为基础搭建自身模块。

      • logback-classic:logback 对于 SLF4j 的实现,其中依赖了 logback-core 包

      • logback-access:集成 Servlet 容器,实现 HTTP 访问日志的功能。

    • 可以输出日志到文件、数据库、控制台中,还可以将日志文件进行压缩,功能很丰富

    • 日志级别(从高到低):FATAL、ERROR、WARNING、INFO、DEBUG、TRACE

    日志级别 日志介绍
    TRACE 在线调试,默认不输出到控制台和文件
    DEBUG 在线调试、终端查看,默认输出到控制台,用于开发者查看日志流水
    INFO 报告程序进度、查看程序状态,用于跟踪程序进展
    WARNING 警告,程序出现错误,但是程序可以恢复,程序仍是正常状态
    ERROR 错误,程序发生错误后还可以运行,但是程序极有可能处于非正常状态,功能可能无法全部完成
    FATAL 致命错误,程序必须马上终止

总结

  • 日志门面和实现框架的面世时间(从早到晚):Log4j -> JUL -> JCL -> SLF4j -> Logback -> Log4j2

  • JCL 门面优先寻找 Log4j 实现,退而求次则是 JUL 实现,最后才会使用内部提供的 SimpleLog 实现

  • 而,SLF4j 门面是作为一个纯粹的日志门面,提供了 SLF4j 桥接器将 SLF4j 桥接到 log4j、JUL 实现去做日志输出

  • 后续 log4j 无法满足高性能要求后,SLF4j 制作者根据 SLF4j 接口写出了 logback 日志实现框架。

  • log4j2 是 Apache 全面借鉴 SLF4j + Logback 后推出的,添加了很多新的特性,还做了分离式设计。

  • 推荐使用 SLF4j + logback 的方式去做 Java 的日志输出

    • 优点一:logback 中实现 SLF4j 门面,在 Java 程序中直接引入 logback-classic 的依赖即可

    • 优点二:SpringBoot 使用 logback 作为默认的日志实现,在 SpringBoot 项目中可以直接使用。

SLF4j + Logback 的实现

第一步:添加配置文件

  • Logback 框架可以自动识别 */classes/ 下的 logback.xml 文件。

    • 在 SpringBoot 框架下,Logback 框架还可以自动识别 */classes/ 下的 logback-spring.xml 文件。
  • 同样,Log4j/Log4j2 框架可以自动识别 */classes/ 下的 log4j.xml/log4j2.xml 文件。

    • 在 SpringBoot 框架下,Log4j2 框架还可以自动识别 */classes/ 下的 log4j2-spring.xml 文件。
  • JUL 框架可以自动识别 */classes/ 下的 logging.properties 文件。

  • 将配置文件放在 src/main/resources 下,项目构建时,文件就会加载到 */classes/ 下了。

logback.xml

<configuration scan="true" scanPeriod="10000">
    
    
    

    

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

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

    
    <property name="layout3"
              value="%level%d{yyyy-MM-dd HH:mm:ss}%c%M%L%thread%msg"/>

    
    <property name="filePath" value="D:/file/projects/files/Java/journal/logback/"/>

    
    <property name="red" value="System.err"/>
    
    <property name="black" value="System.out"/>

    
    <timestamp key="datetime" datePattern="yyyy-MM-dd"/>

    
    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        
        <target>
            ${red}
        target>
        
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>
                ${layout1}
            pattern>
        encoder>
    appender>

    
    <appender name="consoleAppender1" class="ch.qos.logback.core.ConsoleAppender">
        
        <target>
            ${red}
        target>
        
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>
                ${layout2}
            pattern>
            
            <charset>
                UTF-8
            charset>
        encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">-->
            <level>DEBUGlevel>
            <onMatch>ACCEPTonMatch>
            <onMismatch>DENYonMismatch>
        filter>
    appender>

    
    <appender name="consoleFilterAppender" class="ch.qos.logback.core.ConsoleAppender">
        
        <target>
            
            ${black}
        target>
        
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>
                ${layout2}
            pattern>
        encoder>
        
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            
            <level>DEBUGlevel>

            

            
            <onMatch>ACCEPTonMatch>
            
            <onMismatch>DENYonMismatch>
        filter>

    appender>

    
    <appender name="htmlFileAppender" class="ch.qos.logback.core.FileAppender">
        
        <file>
            ${filePath}/${datetime}/logback-html.html
        file>
        
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="ch.qos.logback.classic.html.HTMLLayout">
                <pattern>
                    ${layout3}
                pattern>
            layout>
        encoder>
    appender>

    
    <appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
        
        <file>
            ${filePath}/${datetime}/logback-file.log
        file>
        
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>
                ${layout2}
            pattern>
        encoder>
    appender>

    
    <appender name="rollingFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        
        <file>
            ${filePath}/${datetime}/logback-rollingFile.log
        file>
        
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>
                ${layout2}
            pattern>
            
            <charset>
                UTF-8
            charset>
        encoder>
        
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            

            
            <fileNamePattern>
                
                ${filePath}/${datetime}/logback-rollingFile%i.%d{yyyy-MM-dd}.log.gz
            fileNamePattern>

            
            <maxFileSize>
                10MB
            maxFileSize>

            
            
            

            
            <maxHistory>30maxHistory>

        rollingPolicy>
    appender>

    
    <appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
        
        <appender-ref ref="consoleAppender"/>
        
    appender>

    
    <root level="ALL">
        
        <appender-ref ref="asyncAppender"/>
        <appender-ref ref="fileAppender"/>
    root>

    
    <logger name="com.domain.LogTool" level="trace" additivity="false">
        
        
        <appender-ref ref="consoleAppender1"/>
        <appender-ref ref="htmlFileAppender"/>
        <appender-ref ref="rollingFileAppender"/>
    logger>

configuration>
  • 注意:自定义的 Logger 中 name="com.domain.LogTool",那么 LoggerFactory.getLogger(LogTool.class); 中传入 LogTool.java
    时,才会使用自定义的 Logger

  • 其他情况都是使用默认的 Logger(RootLogger)

  • 当然,自定义的 Logger 中 name="com.domain" 时,LoggerFactory.getLogger(LogTool.class); 中传入 com.domain 内的类
    时,也会使用自定义的 Logger

LogTool.java 工具类(非必要)

public class LogTool {

    private static Logger LOGGER = LoggerFactory.getLogger(Log.class);

    /**
     * 添加自定义的 Logger 包下的 类,使用自定义的 Logger
     *
     * @param c   类的 Class 对象
     * @param  泛型
     */
    public static <T> void setClassC(Class<T> c) {
        if (c != null) {
            LOGGER = LoggerFactory.getLogger(c);
        }
    }

    /**
     * 使用自定义的 Logger
     *
     * @param LOGGER Logger
     */
    public static void setLOGGER(Logger LOGGER) {
        Log.LOGGER = LOGGER;
    }

    /**
     * 输出 日志信息(类对象版)
     *
     * @param object   类对象
     * @param logLevel 日志级别
     */
    public static void outputLog(Object object, LogLevel logLevel) {
        switch (logLevel) {
            case Trace:
                LOGGER.trace("追踪 消息 - {}", object);
                break;
            case Debug:
                LOGGER.debug("详细 消息 - {}", object);
                break;
            case Info:
                LOGGER.info("关键 消息 - {}", object);
                break;
            case Warn:
                LOGGER.warn("警告 消息 - {}", object);
                break;
            case Error:
                LOGGER.error("错误 消息 - {}", object);
                break;
            default:
                break;
        }
    }

    /**
     * 输出 日志信息(信息版)
     *
     * @param message  消息
     * @param logLevel 日志级别
     */
    public static void outputLog(String message, LogLevel logLevel) {
        switch (logLevel) {
            case Trace:
                LOGGER.trace("追踪 消息 - { " + message + " }");
                break;
            case Debug:
                LOGGER.debug("详细 消息 - { " + message + " }");
                break;
            case Info:
                LOGGER.info("关键 消息 - { " + message + " }");
                break;
            case Warn:
                LOGGER.warn("警告 消息 - { " + message + " }");
                break;
            case Error:
                LOGGER.error("错误 消息 - { " + message + " }");
                break;
            default:
                break;
        }
    }


    /**
     * Log 级别 枚举
     */
    public enum LogLevel {
        /**
         * 追踪
         */
        Trace,
        /**
         * 调试、详细
         */
        Debug,
        /**
         * 关键、消息
         */
        Info,
        /**
         * 警告
         */
        Warn,
        /**
         * 错误
         */
        Error
    }

}

依赖导入

Java 程序
  • Logback-classic 依赖包含了 SLF4j 和 Logback-core 的依赖,导入 Logback-classic 一个就可以了。

<dependencies>
    <dependency>
        <groupId>ch.qos.logbackgroupId>
        <artifactId>logback-classicartifactId>
        <version>1.2.3version>
    dependency>
dependencies>
SpringBoot 项目
  • spring-boot-starter 依赖中包含了 SLF4j 和 Logback,无需导入,当然也可以导入新的 logback 覆盖默认的版本。

<dependencies>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starterartifactId>
        <version>2.4.5version>
    dependency>

    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
        <version>2.4.5version>
    dependency>

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <version>2.4.5version>
        <scope>testscope>
    dependency>
dependencies>

测试


<dependencies>
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>RELEASEversion>
        <scope>testscope>
    dependency>
dependencies>
public class LogbackTest {

    /**
     * 测试 工具类 Log
     */
    @Test
    public void test() {

        /**
         * root Logger
         */
        Log.setLOGGER(LoggerFactory.getLogger(LogbackTest.class));
        for (int i = 0; i < 100; i++) {
            Log.outputLog("trace", Log.LogLevel.Trace);
            Log.outputLog("debug", Log.LogLevel.Debug);
            Log.outputLog("info", Log.LogLevel.Info);
            Log.outputLog("warn", Log.LogLevel.Warn);
            Log.outputLog("error", Log.LogLevel.Error);
        }
    }

    /**
     * 测试 工具类 Log
     */
    @Test
    public void test1() {

        /**
         * 自定义 Logger
         */
        for (int i = 0; i < 100; i++) {
            Log.outputLog("trace", Log.LogLevel.Trace);
            Log.outputLog("debug", Log.LogLevel.Debug);
            Log.outputLog("info", Log.LogLevel.Info);
            Log.outputLog("warn", Log.LogLevel.Warn);
            Log.outputLog("error", Log.LogLevel.Error);
        }
    }
}

其他的日志门面与实现框架

  • 第二建议使用的日志框架是:SLF4j + Log4j2。

  • 相应的源码可以到 Gitee 上查看,地址:LJM/journal

你可能感兴趣的:(通用的知识,Java,学习笔记,java,log4j,slf4j,logback,log4j2)