前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 日志

Java 日志

作者头像
廿四
发布2023-02-28 23:09:43
7370
发布2023-02-28 23:09:43
举报
文章被收录于专栏:通用通用

Java 日志

  • 日志门面:提供统一的日志输出接口
  • 日志实现:具体实现日志输出的代码

日志门面与实现框架

  • 使用 日志门面+日志实现框架 的方式,是为了:低耦合,日志的实现与业务代码通过 日志门面连接,在后续修改日志实现时,无需更改业务代码。
  • 这是 门面设计模式(外观设计模式)的典型应用。

日志门面

  • SLF4j(Simple Logging Facade For Java):一个为 Java 程序提供的统一日志输出接口,就是一个接口,
  • JCL(Jaka Commons Logging, Apache Commons Logging):Apache 提供的一个日志门面,提供统一的对外接口。

日志实现框架

  • JUL(Java util Logging)Java 原生的日志框架,使用时不需要引用第三方类库,使用方便
代码语言:txt
复制
- 7 个日志级别(从高到低):**SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST**。
代码语言:txt
复制
- 同时还有 **OFF、ALL 两个特别的日志级别,用来 关闭/打开 所有的日志**。
  • log4jApache 的一个开源项目
代码语言:txt
复制
- 7 个日志级别(从高到低):**OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL**。

日志级别

日志介绍

OFF

最高日志级别,关闭所有日志

FATAL

将会导致引用程序退出的错误

ERROR

发生错误事件,但仍不影响系统的继续运行

WARN

警告,存在潜在的错误

INFO

一般用在粗粒度级别上,强调应用程序的运行全程

DEBUG

一般用在细粒度级别上,用于调试应用程序

ALL

最低日志级别,打开所有日志

  • log4j2log4j 的升级版,参考了 logback 的设计,同时进行了问题修复
代码语言:txt
复制
- **异常优化**:提供了一些异常处理机制,来解决在 logback 中,应用无法感知到 Appener 异常。
代码语言:txt
复制
- **性能提升**:相较于 log4j 和 logback,性能都有明显的提升。
代码语言:txt
复制
- **自动重载配置**:参考 logback 的参数修改自动更新机制,提供自动刷新参数的设置。
代码语言:txt
复制
- **无垃圾机制**:可以使用其设计的一套无垃圾机制(对象重用、内存缓冲),避免频繁的日志记录导致 JVM gc 压力过大。
  • logbackSpringBoot 默认的日志框架
代码语言:txt
复制
- 由三个模块组成:
代码语言:txt
复制
    - logback-core:logback 核心包,开发人员可以以次为基础搭建自身模块。
代码语言:txt
复制
    - logback-classic:**logback 对于 SLF4j 的实现,其中依赖了 logback-core 包**。
代码语言:txt
复制
    - logback-access:集成 Servlet 容器,实现 HTTP 访问日志的功能。
代码语言:txt
复制
- **可以输出日志到文件、数据库、控制台中,还可以将日志文件进行压缩,功能很丰富**。
代码语言:txt
复制
- 日志级别(从高到低):**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 的日志输出
代码语言:txt
复制
- 优点一:**logback 中实现 SLF4j 门面,在 Java 程序中直接引入 logback-classic 的依赖即可**。
代码语言:txt
复制
- 优点二:**SpringBoot 使用 logback 作为默认的日志实现**,在 SpringBoot 项目中可以直接使用。

SLF4j + Logback 的实现

第一步:添加配置文件

  • Logback 框架可以自动识别 */classes/ 下的 logback.xml 文件。
代码语言:txt
复制
- 在 SpringBoot 框架下,Logback 框架还可以自动识别 */classes/ 下的 ``logback-spring.xml`` 文件。
  • 同样,Log4j/Log4j2 框架可以自动识别 */classes/ 下的 log4j.xml/log4j2.xml 文件。
代码语言:txt
复制
- 在 SpringBoot 框架下,Log4j2 框架还可以自动识别 */classes/ 下的 ``log4j2-spring.xml`` 文件。
  • JUL 框架可以自动识别 */classes/ 下的 logging.properties 文件。
  • 将配置文件放在 src/main/resources 下,项目构建时,文件就会加载到 */classes/ 下了。
logback.xml
代码语言:html
复制
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="10000">
    <!-- scan 属性:当此属性设置为 true 时,配置文件如果发生改变,将会被重新加载,默认值为 true -->
    <!--
        scanPeriod属性:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。
        当 scan 为 true 时,此属性生效。默认的时间间隔为 1 分钟。
    -->
    <!-- debug 属性:当此属性设置为 true 时,将打印出 logback 内部日志信息,实时查看 logback 运行状态。默认值为 false。 -->

    <!--
          日志格式设置标识符:
          %n:换行符;
          %level:日志级别;
          %-5level:日志级别(使用5个字符,并靠左对齐);
          %msg、%m:日志消息(简写是:%m);
          %logger、%M:日志输出者(就是哪个类做这个日志输出,这个应用是 Log 类);
          %c:日志输出者输出日志时使用的方法;
          %L:日志输出者输出日志时使用的方法中具体的行数;
          %d{yyyy-MM-dd HH:mm:ss}:时间,大括号内的是时间格式,默认格式是 2023-02-27 15:15:01,877;
          %thread:输出日志的进程名字;
      -->

    <!-- 日志 输出格式 1 -->
    <property name="layout1" value="[%level] -- %m -- [%d{yyyy-MM-dd HH:mm:ss}] %c %M %L [%thread] %n"/>

    <!-- 日志 输出格式 2 -->
    <property name="layout2"
              value="[%-5level] - %d{yyyy-MM-dd HH:mm:ss} - %c:%M:%L %n[%-5level] - [%thread] - %msg %n%n"/>

    <!-- 输出格式 3(输出到 *.html 文件,不需要间隔) -->
    <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"/>

    <!--  每天的日期 String 串,用来生成文件夹  -->
    <timestamp key="datetime" datePattern="yyyy-MM-dd"/>

    <!--
          配置 输出控制器:consoleAppender,控制台输出
      -->
    <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>

    <!--
        配置 输出控制器:consoleAppender1,控制台输出
    -->
    <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>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--
        配置 输出控制器:consoleFilterAppender,控制台输出
        在 consoleAppender 的基础上 添加 过滤器
    -->
    <appender name="consoleFilterAppender" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 颜色 -->
        <target>
            <!-- ${red} -->
            ${black}
        </target>
        <!-- 输出格式 -->
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>
                ${layout2}
            </pattern>
        </encoder>
        <!-- 配置过滤器 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 设置 级别 -->
            <level>DEBUG</level>

            <!--
                功能是:仅记录 设定好的级别的日志,可以用来设置 不同的日志输出到不同的 日志文件中;
                当然,将配置值进行 对调后,就可以反向屏蔽(屏蔽指定级别的日志)
            -->

            <!-- 设置 级别 与 设置的级别 匹配时 就 打印 -->
            <onMatch>ACCEPT</onMatch>
            <!-- 设置 级别 与 设置的级别 不匹配时 就 屏蔽 -->
            <onMismatch>DENY</onMismatch>
        </filter>

    </appender>

    <!--
        配置 输出控制器:htmlFileAppender,输出到 .html 文件
    -->
    <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>

    <!--
        配置 输出控制器:fileAppender,输出到 .log 文件
    -->
    <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>

    <!--
            配置 输出控制器:rollingFileAppender,输出到 .log 文件,可以 拆分 和 压缩
    -->
    <appender name="rollingFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--
            文件位置
            ${filePath}:是文件存放目录路径,即:D:/file/projects/files/Java/journal/logback/
            ${datetime}:是在文件存放目录路径下的文件夹名称,即:yyyy-MM-dd
            logback-rollingFile.log:文件名(包括后缀)
        -->
        <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">
            <!-- 默认格式:gz,-->

            <!-- 声明文件名:按照 时间和格式 来 -->
            <fileNamePattern>
                <!-- %d{yyyy-MM-dd}是时间,%i 是防止同一天的文件重名(而 压缩包 有 *.gz、*.zip 等等) -->
                ${filePath}/${datetime}/logback-rollingFile%i.%d{yyyy-MM-dd}.log.gz
            </fileNamePattern>

            <!-- 单个文件最大的的大小:按照 文件大小 来拆分文件 -->
            <maxFileSize>
                10MB
            </maxFileSize>

            <!-- 文件数目(单位:个):大于 文件数目后 将会覆盖更早的日志压缩文件(不建议添加) -->
            <!--            <minIndex>1</minIndex>-->
            <!--            <maxIndex>3</maxIndex>-->

            <!-- 文件保存最长时间(单位:天):过早的日志压缩文件将会删除 -->
            <maxHistory>30</maxHistory>

        </rollingPolicy>
    </appender>

    <!-- 配置异步 appender-->
    <appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 导入实际的 Appender -->
        <appender-ref ref="consoleAppender"/>
        <!--
            注意:这两个属性不能乱配置
                1、<discardingThreshold></discardingThreshold>
                阈值,
                当队列剩余容量小于阈值时,trace debug info 这三个级别的日志将被抛弃,
                默认值是:-1
                2、<queueSize></queueSize>
                队列的深度
                默认值是:256
        -->
    </appender>

    <!--
        配置 日志记录器:root Logger
    -->
    <root level="ALL">
        <!-- 引入 Appender -->
        <appender-ref ref="asyncAppender"/>
        <appender-ref ref="fileAppender"/>
    </root>

    <!-- 自定义 Logger -->
    <logger name="com.domain.LogTool" level="trace" additivity="false">
        <!--
            additivity="false"  表示 不继承 rootLogger
        -->
        <!-- 引入 Appender -->
        <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 工具类(非必要)

代码语言:java
复制
public class LogTool {

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

    /**
     * 添加自定义的 Logger 包下的 类,使用自定义的 Logger
     *
     * @param c   类的 Class 对象
     * @param <T> 泛型
     */
    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 一个就可以了。
代码语言:html
复制
<dependencies>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
</dependencies>
SpringBoot 项目
  • spring-boot-starter 依赖中包含了 SLF4j 和 Logback,无需导入,当然也可以导入新的 logback 覆盖默认的版本。
代码语言:html
复制
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>2.4.5</version>
    </dependency>

    <!--Spring boot Web容器-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.4.5</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <version>2.4.5</version>
        <scope>test</scope>
    </dependency>
</dependencies>

测试

代码语言:html
复制
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>RELEASE</version>
        <scope>test</scope>
    </dependency>
</dependencies>
代码语言:java
复制
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

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Java 日志
    • 日志门面与实现框架
      • 日志门面
      • 日志实现框架
      • 总结
    • SLF4j + Logback 的实现
      • 第一步:添加配置文件
      • LogTool.java 工具类(非必要)
      • 依赖导入
      • 测试
    • 其他的日志门面与实现框架
    相关产品与服务
    日志服务
    日志服务(Cloud Log Service,CLS)是腾讯云提供的一站式日志服务平台,提供了从日志采集、日志存储到日志检索,图表分析、监控告警、日志投递等多项服务,协助用户通过日志来解决业务运维、服务监控、日志审计等场景问题。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档