跳转至

日志

log.info、log.error就是打日志,日志就是记录程序运行时的状态和信息,当系统出现问题时,可以通过日志快速定位问题。

MyBatis框架内置日志工厂。日志工厂负责自动加载项目中配置的日志。MyBatis支持以下日志:

  • slf4j
  • Apache Commons Logging
  • Log4j 2
  • Log4j (deprecated since 3.5.9)
  • JDK logging

pom.xml增加log4j的依赖:

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

在resources中新建log4j.properties配置文件。名称必须叫这个名字,扩展名必须是.properties。

如果只是想看sql执行过程,那么可以整体调高,局部降低:

将整个日志级别调为ERROR,然后mapper.xml涉及的内容级别降低为TRACE。这样整体的多余信息不会输出,然后mapper.xml中的涉及内容会详细打印。

log4j.properties

# log4j中定义的级别
log4j.rootLogger=ERROR , console , D 

# log4j.logger是固定的,a.b.c是命名空间的名字可以只写一部分。
log4j.logger.a.b=TRACE
# log4f.logger是固定的,com.hh.mapper.BookMapper是命名空间的名字。
# log4j.logger.com.hh.mapper.BookMapper=TRACE

### console ###
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%p] [%-d{yyyy-MM-dd HH\:mm\:ss}] %C.%M(%L) | %m%n
### log file ###
log4j.appender.D=org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File=D:/log4j.log
log4j.appender.D.Append=true
# 只能生级别,不能降
log4j.appender.D.Threshold=INFO
log4j.appender.D.layout=org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern=[%p] [%-d{yyyy-MM-dd HH\:mm\:ss}] %

logback

sprint boot默认集成了Logback日志框架。直接使用即可,不用引用额外的库。

依赖

<dependencies>
    <!-- SLF4J 核心 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.9</version>
    </dependency>
    <!-- Logback 实现 -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.4.11</version>
    </dependency>
</dependencies>

logback.xml

通过修改日志配置文件

  • 可以把日志同时输出到控制台和文件中。
  • 设置日志格式。
  • 控制日志级别。
<?xml version="1.0" encoding="UTF-8"?>

<configuration>
    <!--定义日志文件的存储地址,使用绝对路径-->
    <property name="LOG_HOME" value="e:/logs"/>

    <!-- Console 输出设置 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <fileNamePattern>${LOG_HOME}/leadnews.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxFileSize>100MB</maxFileSize>
            <!-- 只保留近30天的日志 -->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!-- 日志压缩 -->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 异步输出 -->
    <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 丢弃阈值,0表示不丢弃。默认如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 队列大小。更改默认的队列的深度,该值会影响性能.默认值为256 -->
        <queueSize>512</queueSize>
        <!-- 队列满时是否阻塞,false表示会阻塞 -->
        <neverBlock>false</neverBlock>
        <!-- 添加附加的appender,最多只能添加一个 -->
        <appender-ref ref="FILE"/>
    </appender>

    <logger name="org.apache.ibatis.cache.decorators.LoggingCache" level="DEBUG" additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>
    <logger name="org.springframework.boot" level="debug"/>

    <!-- 设置日志级别为info -->
    <root level="info">
        <!--<appender-ref ref="ASYNC"/>-->
        <appender-ref ref="FILE"/>
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>

使用

1、通过LoggerFactory手动获取Logger日志对象

public class MyService {
    private static final Logger logger = LoggerFactory.getLogger(MyService.class);
    public void doSomething() {
        logger.info("Doing something in MyService");
    }
}

2、使用this.getClass()获取当前类的类型,创建Logger对象

public class MyService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    public void doSomething() {
        logger.info("Doing something in MyService");
    }
}

简单方法

使用Lombok工具库提供的@Slf4j注解,类上面引入日志:@Slf4j,可以自动为当前类生成日志对象,不用手动定义了。

log.info("用户名已存在");
log.error("业务操作失败", e); // 推荐写法(包含堆栈)
log.error("操作失败: {}", e.getMessage()); // 仅记录消息(无堆栈)

日志分级

  • trace(跟踪信息):最细粒度的信息,通常只在开发过程中使用,用于跟踪程序的执行路径。
  • debug(调试信息):记录程序运行时的内部状态和变量值。
  • info(普通信息) :记录系统的关键运行状态和业务流程。
  • warn(警告信息) :表示可能存在潜在问题,但系统仍可继续运行。
  • error(错误信息) :表示出现了影响系统功能的问题,需要及时处理。
  • fatal(致命错误)

如果所有信息都用同一级别,出了问题时不容易快速找到错误信息。

在生产环境通常会把日志级别调高,这样debug级别的日志就不会输出了。防止重要信息被无用日志淹没

记录日志信息

log.error("操作失败: {}", e.getMessage()); // 仅记录消息(无堆栈)

上面的{}叫参数化日志,{}是一个占位符,日志框架会在运行时自动把后面的参数值替换进去。

方法执行前、业务方法、方法执行后。利用AOP切面编程,自动给每个业务方法的执行前后添加日志,这样就不会错过任何一次调用信息了。

统一日志格式

在日志配置文件中定义统一日志格式,包含时间戳、线程名称、日志级别,类名、方法名、具体内容等关键信息。

还可以通过MDC给日志添加额外的上下文信息,比如:请求id、用户id等等。方便追踪。

异步日志

正常情况下调用log.info()打日志时,程序会立刻把日志写入文件,这个过程是同步的,会阻塞当前线程,而异步日志会把写日志的操作放到另一个线程中去做,不会阻塞主线程,性能更好。

异步日志缺点:如果程序突然崩溃,缓冲区中还没来得及写入文件的日志可能会丢失。

日志管理

自动清理日志,防止占过多空间。

    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <fileNamePattern>${LOG_HOME}/leadnews.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxFileSize>100MB</maxFileSize>
            <!-- 只保留近30天的日志 -->
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <!-- 日志压缩 -->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

集成日志收集系统

对于分布式系统,要用专业的日志收集系统,比如:ELK。

spring-boot-starter-web、spring-boot-starter-security等都包含有日志,需要排除:

<!-- 排除默认的日志-->
<exclusions>
    <exclusion>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
    </exclusion>
</exclusions>