一、日志的作用

程序中的日志是记录程序的运行情况,包括用户的各种操作、程序的运行状态等信息。类似于飞机的黑匣子。

二、日志的级别

表:日志级别及其描述

日志级别

描述

OFF

关闭:不输出日志

FATAL

致命:用于输出可能会导致应用程序终止的错误

ERROR

错误:用于输出程序的错误(这些错误不会导致程序崩溃)

WARN

警告:用于输出警告信息,提示可能出现的问题

INFO

信息:用于输出程序运行过程的详细信息

DEBUG

调试:用于输出调试应用有用的信息

TRACE

跟踪:用于输出更细致的程序运行轨迹

ALL

所有:用于输出所有级别的信息


常用日志级别:

我们在日常使用中的常用日志级别是DEBUG、INFO、WARN、ERROR。

ERROR

ERROR级别虽然不至于程序立刻奔溃,但是也影响了用户的正常使用了,当出现这个级别的日志的时候我们应该立即处理。

WARN

WARN级别的日志通常用来记录用户操作不当,或者网络不稳定而出现的非预期情况。

INFO

INFO是默认的日志级别,用来记录程序的正常运行记录。

DEBUG

DEBUG级别的日志会非常详细的输出程序的运行情况。但是这个级别过多的日志输出会严重消耗资源,影响程序性能。

三、日志配置

我们的日志配置主要有两种方式:一种是直接在application.yml文件中配置,另外一种是在外置logback-spring.xml文件中配置。

2023-07-24 15:09:42.580  INFO 7532 --- [nio-8080-exec-5] c.e.f.exception.GlobalExceptionHandler   : 全局异常处理...

可以看到,默认的情况下一条日志由下面几个部分组成。

日期时间

日志级别

进程ID

--分隔符

[xxx]线程名

类路径

日志消息

application.yml文件配置方式:

#自定义日志配置
logging.pattern.console="%d - %m"%n

控制台输出了我们自定义的日志格式

"2023-07-24 15:20:38,322 - Tomcat started on port(s): 8080 (http) with context path ''"
"2023-07-24 15:20:38,333 - Started FunlyDemoApplication in 4.744 seconds (JVM running for 6.767)"
"2023-07-24 15:20:44,923 - Initializing Spring DispatcherServlet 'dispatcherServlet'"
"2023-07-24 15:20:44,923 - Initializing Servlet 'dispatcherServlet'"
"2023-07-24 15:20:44,924 - Completed initialization in 1 ms"
"2023-07-24 15:20:44,949 - info日志..."

logback-spring.xml文件配置方式:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。
                 当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds">
    <contextName>logback</contextName>
    <!-- 4. 最终的策略 -->
    <!-- 4.1 开发环境:打印控制台-->

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

    <!--指定对应环境的日志的地址,下面的会覆盖上面的-->
    <springProfile name="dev">
        <logger name="com" level="debug"/>
        <logger name="spepc" level="debug"/>
    </springProfile>
    <!--指定对应环境的日志的地址,下面的会覆盖上面的-->
    <springProfile name="test">
        <property name="log.path" value="D:/spepc_iot_test/dzzh_logs_test"/>
        <logger name="com" level="info"/>
        <logger name="org" level="info"/>
        <logger name="spepc" level="info"/>
    </springProfile>
    <!--指定对应环境的日志的地址,下面的会覆盖上面的-->
    <springProfile name="prod">
        <property name="log.path" value="./dzzh_logs_prod"/>
        <logger name="spepc" level="info"/>
        <logger name="com" level="info"/>
    </springProfile>
    <springProfile name="yongzhou">
        <property name="log.path" value="./dzzh_logs_yongzhou"/>
        <logger name="spepc" level="info"/>
        <logger name="com" level="info"/>
    </springProfile>

    <!--0. 日志格式和颜色渲染 -->
    <!-- 彩色日志依赖的渲染类 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
    <conversionRule conversionWord="wex"
                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
    <conversionRule conversionWord="wEx"
                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
    <!-- 彩色日志格式 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

    <!--1. 输出到控制台-->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
        <encoder>
            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
            <!-- 设置字符集 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!--2. 输出到文档-->
    <!-- 2.1 level为 CONSOLE_FILE 日志,时间滚动输出  -->
    <appender name="CONSOLE_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${log.path}/console.log</file>
        <!--日志文档输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 日志归档 -->
            <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/console-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>50MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
    </appender>

    <!-- 2.4 level为 ERROR 日志,时间滚动输出  -->
    <appender name="SySErr" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${log.path}/sysErr.log</file>
        <!--日志文档输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/sysErr-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
        <!-- 此日志文档只记录ERROR级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>


    <appender name="Protocol" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
            <evaluator
                    class="ch.qos.logback.classic.boolex.JaninoEventEvaluator"> <!-- defaults to type ch.qos.logback.classic.boolex.JaninoEventEvaluator -->
                <expression>logger.contains("Protocol")</expression>
            </evaluator>
            <OnMismatch>DENY</OnMismatch>
            <OnMatch>ACCEPT</OnMatch>
        </filter>
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${log.path}/protocol.log</file>
        <!--日志文档输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/protocol-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
    </appender>

    <appender name="Mqtt" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
            <evaluator
                    class="ch.qos.logback.classic.boolex.JaninoEventEvaluator"> <!-- defaults to type ch.qos.logback.classic.boolex.JaninoEventEvaluator -->
                <expression>logger.contains("Mqtt")</expression>
            </evaluator>
            <OnMismatch>DENY</OnMismatch>
            <OnMatch>ACCEPT</OnMatch>
        </filter>
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${log.path}/mqtt.log</file>
        <!--日志文档输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/mqtt-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
    </appender>


    <appender name="rocketmq" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
            <evaluator
                    class="ch.qos.logback.classic.boolex.JaninoEventEvaluator"> <!-- defaults to type ch.qos.logback.classic.boolex.JaninoEventEvaluator -->
                <expression>logger.contains("rocketmq")</expression>
            </evaluator>
            <OnMismatch>DENY</OnMismatch>
            <OnMatch>ACCEPT</OnMatch>
        </filter>
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${log.path}/rocketmq.log</file>
        <!--日志文档输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/rocketmq-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
    </appender>

    <appender name="Unistrong" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
            <evaluator
                    class="ch.qos.logback.classic.boolex.JaninoEventEvaluator"> <!-- defaults to type ch.qos.logback.classic.boolex.JaninoEventEvaluator -->
                <expression>logger.contains("Unistrong")</expression>
            </evaluator>
            <OnMismatch>DENY</OnMismatch>
            <OnMatch>ACCEPT</OnMatch>
        </filter>
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${log.path}/unistrong.log</file>
        <!--日志文档输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/unistrong-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
    </appender>

    <appender name="Paiguang" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文档的路径及文档名 -->
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
            <evaluator
                    class="ch.qos.logback.classic.boolex.JaninoEventEvaluator"> <!-- defaults to type ch.qos.logback.classic.boolex.JaninoEventEvaluator -->
                <expression>logger.contains("Paiguang")</expression>
            </evaluator>
            <OnMismatch>DENY</OnMismatch>
            <OnMatch>ACCEPT</OnMatch>
        </filter>
        <!-- 正在记录的日志文档的路径及文档名 -->
        <file>${log.path}/paiguang.log</file>
        <!--日志文档输出格式-->
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
        </encoder>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/paiguang-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <!--日志文档保留天数-->
            <maxHistory>15</maxHistory>
        </rollingPolicy>
    </appender>


    <appender name="RocketmqClientAppender"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}/rocketmq_client.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/rocketmq_client.log.%d{yyyy-MM-dd}.%i.log
            </fileNamePattern>
            <TimeBasedFileNamingAndTriggeringPolicy
                    class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <MaxFileSize>30MB</MaxFileSize>
            </TimeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder charset="UTF-8">
            <pattern>%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{0} %X{ServiceId} - %m%n</pattern>
        </encoder>
    </appender>

    <logger name="RocketmqClient" additivity="false">
        <level value="info" />
        <appender-ref ref="RocketmqClientAppender"/>
    </logger>

    <root level="info">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="CONSOLE_FILE"/>
        <appender-ref ref="Protocol"/>
        <appender-ref ref="rocketmq"/>
        <appender-ref ref="SySErr"/>
        <appender-ref ref="Unistrong"/>
        <appender-ref ref="Paiguang"/>
    </root>
    <springProfile name="prod">
        <root level="info">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="CONSOLE_FILE"/>
            <appender-ref ref="Protocol"/>
            <appender-ref ref="rocketmq"/>
            <appender-ref ref="SySErr"/>
            <appender-ref ref="Unistrong"/>
            <appender-ref ref="Paiguang"/>
        </root>
    </springProfile>
</configuration>

我们配置了日志的输出路径、日志输出格式、日志保留时间和日志文件大小等。还可以根据不同的环境使用不同的日志配置。

四、日志规范

使用占位符:

log.info("username:{},password:{}","admin","123456");

控制台输出:

2023-07-24 15:36:28.571  INFO 8876 --- [           main] c.e.funlydemo.FunlyDemoApplicationTests  : username:admin,password:123456

完整的堆栈信息:

 log.error("发生错误:{}",e.getMessage(),e);

正确的使用日志级别:

对于普通的日志记录我们需要INFO级别,而对于错误异常的日志我们不能使用INFO级别日志。

使用Lombok自带的@Slf4j注解

我们使用这个注解可以规范我们的日志书写。