浅谈spring 后端项目配置logback日志

日志

Java boy 后端项目日志一般用 logback or Log4j & Log4j2,spring 默认集成logback,本文主要以logback 展开例子说明。

对于日志系统有什么样的需求?

1、本地环境需要打印Console日志。

2、测试环境最好实时打印日志。

3、生产环境打印日志不要阻塞系统(日志阻塞导致很多故障,特别是流量大的场景,小流量可以忽略)

4、可以实时修改,有些日志不关注,需要隔离一下。

5、分离日志...ERROR ALL not care...

... 等等信息。

日志需求分析

"小小"《环境》

现实开发场景中,很多都需要有环境的概念,redis 预发和生产 prefix key 不同.

test 环境和生产环境的调用的接口不同... env=test env=prod 等等配置标识。

但是这些都不是非常的规范,在Spring Boot中,spring.profiles.active是一个非常重要的配置属性,它用于指定当前激活的配置文件(profiles)。通过这个属性,你可以控制应用程序在不同环境下的行为,例如开发、测试和生产环境。 SpringBoot激活profiles你知道几种方式?

  • application.properties
properties 复制代码
spring.profiles.active=dev
  • 系统参数
bash 复制代码
export SPRING_PROFILES_ACTIVE=dev
  • 命令行参数
bash 复制代码
java -jar your-application.jar --spring.profiles.active=prod

通过配置中心注入系统环境或者属性进行配置(放在公共配置,多应用统一使用),或者统一运维的部署脚本中指定,通过标准化的路径实现环境标准化,无需自定义。

环境隔离日志配置

这个是 logback-spring.xml 里面的配置

非生产、非预发、非测试 非开发环境打印console

springProfile 灵活的配置,可以让开发处理多环境的配置信息非常easy

xml 复制代码
 <springProfile name="!prod,!pre">
        <root level="INFO">
            <appender-ref ref="APPLICATION"/>
            <appender-ref ref="FILE_ERROR"/>
            <!--本地环境打印console 日志-->
            <springProfile name="!test,!dev">
                <appender-ref ref="CONSOLE"/>
            </springProfile>
        </root>
    </springProfile>

日志阻塞

之前见过很多系统由于日志阻塞导致系统功能受影响,响应耗时增加,这样的例子非常多。常见的场景的处理方式是配置为异步日志。 logback之 AsyncAppender 的原理、源码及避坑建议

在普通的日志RollingFileAppender 基础上包装一个异步日志 ,如下配置。

xml 复制代码
<appender name="APPLICATION" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/application.log</file>
        <encoder>
            <pattern>${PATTERN}</pattern>
        </encoder>
        <!-- 滚动策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 路径 -->
            <fileNamePattern>logs/info.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>1GB</maxFileSize>
            <maxHistory>30</maxHistory>
            <totalSizeCap>10GB</totalSizeCap>
        </rollingPolicy>
</appender>
<appender name="ASYNC_APPLICATION" class="ch.qos.logback.classic.AsyncAppender">
            <neverBlock>true</neverBlock>
            <queueSize>5120</queueSize>
            <discardingThreshold>20</discardingThreshold>
            <includeCallerData>true</includeCallerData>
            <appender-ref ref="APPLICATION"/>
</appender>

异步日志,由于打印日志是异步的,导致测试环境跟踪的时候查看日志非实时写文件的,用起来非常恼火,特别是调试的时候,测试环境是否可以非异步的?

通过上面介绍的spring环境隔离即可完成,如下配置。

xml 复制代码
   <!--    prod 和 pre 进行异步日志-->
    <springProfile name="prod,pre">
        <appender name="ASYNC_APPLICATION" class="ch.qos.logback.classic.AsyncAppender">
            <neverBlock>true</neverBlock>
            <queueSize>5120</queueSize>
            <discardingThreshold>20</discardingThreshold>
            <includeCallerData>true</includeCallerData>
            <appender-ref ref="APPLICATION"/>
        </appender>
        <root level="INFO">
            <appender-ref ref="ASYNC_APPLICATION"/>
        </root>
    </springProfile>
  
     <!--test 环境和本地 不进行异步日志-->
    <springProfile name="!prod,!pre">
        <root level="INFO">
            <appender-ref ref="APPLICATION"/>
            <!--本地环境打印console 日志-->
            <springProfile name="!test,!dev">
                <appender-ref ref="CONSOLE"/>
            </springProfile>
        </root>
    </springProfile>

怎么判断异步日志是否配置OK? debug ?arthas 也行?

查看一下异步日志的实例是否存在

bash 复制代码
[arthas@34932]$ vmtool -x  1 --action getInstances --className ch.qos.logback.classic.AsyncAppender  --limit 5
@AsyncAppender[][
    @AsyncAppender[ch.qos.logback.classic.AsyncAppender[ASYNC_LOG_FILE_OKHTTP]],
    @AsyncAppender[ch.qos.logback.classic.AsyncAppender[ASYNC_LOG_FILE_MIDDLEWARE]],
    @AsyncAppender[ch.qos.logback.classic.AsyncAppender[ASYNC_ERROR]],
    @AsyncAppender[ch.qos.logback.classic.AsyncAppender[ASYNC_APPLICATION]],
]

logback 配置文件不推荐放在配置中心

配置文件和源码放在一起,通过环境变量区分不同环境的处理方式比较好,方便修改

某些场景需要隐藏某些日志,放在配置中心非常不方便,修改起来也非常麻烦,修改错误了也不知道,只有发布的时候才能感知。

eg: kafka的日志不要放在all 日志里面,需要在线日志系统订阅关键的业务日志信息,减少费用。

xml 复制代码
<logger name="org.apache.kafka" level="INFO" additivity="false">
        <appender-ref ref="LOG_FILE_MIDDLEWARE"/>
</logger>

eg: 我要禁用日志 ,这种想法可能跟随者业务需求进行变化。

xml 复制代码
 <logger name="org.redisson.connection.DNSMonitor" level="OFF"/>

分离日志

  • additivity="false" 不加入总日志信息
  • error 日志分离
xml 复制代码
<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/application_error.log</file>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder>
            <pattern>${PATTERN}</pattern>
        </encoder>
        <!-- 滚动策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 路径 -->
            <fileNamePattern>logs/application_error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>1GB</maxFileSize>
            <maxHistory>3</maxHistory>
            <totalSizeCap>3GB</totalSizeCap>
        </rollingPolicy>
</appender>

总结

日志系统平常开发过程中经常遇到,日志打印刷屏、日志太多、日志治理等发生在日常的开发过程中,养成一个好的习惯,好的日志更快速的排查到问题。

线上环境error日志重点关注,前提是error日志要少,如果一个error一天几百个G,会有人看?

附上完整日志配置

xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
  
    <property name="PATTERN"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}]  %X{method} %-5level [%thread] [%logger:%line] :%m %n"/>
  
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 采用Spring boot中默认的控制台彩色日志输出模板 -->
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>
  
    <appender name="APPLICATION" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/application.log</file>
        <encoder>
            <pattern>${PATTERN}</pattern>
        </encoder>
        <!-- 滚动策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 路径 -->
            <fileNamePattern>logs/info.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>1GB</maxFileSize>
            <maxHistory>30</maxHistory>
            <totalSizeCap>10GB</totalSizeCap>
        </rollingPolicy>
    </appender>
  
  
    <appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/application_error.log</file>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder>
            <pattern>${PATTERN}</pattern>
        </encoder>
        <!-- 滚动策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 路径 -->
            <fileNamePattern>logs/application_error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>1GB</maxFileSize>
            <maxHistory>3</maxHistory>
            <totalSizeCap>3GB</totalSizeCap>
        </rollingPolicy>
    </appender>
  
    <appender name="LOG_FILE_OKHTTP" class="ch.qos.logback.core.rolling.RollingFileAppender" additivity="false">
        <file>logs/application_okhttp.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>logs/application_okhttp.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>1GB</maxFileSize>
            <maxHistory>2</maxHistory>
            <totalSizeCap>2GB</totalSizeCap>
        </rollingPolicy>
        <!-- 格式化输出 -->
        <encoder>
            <pattern>${PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
  
    <appender name="LOG_FILE_MIDDLEWARE" class="ch.qos.logback.core.rolling.RollingFileAppender" additivity="false">
        <immediateFlush>false</immediateFlush>
        <file>logs/application_middleware_client.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>logs/application_middleware_client.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>1GB</maxFileSize>
            <maxHistory>2</maxHistory>
            <totalSizeCap>2GB</totalSizeCap>
        </rollingPolicy>
        <!-- 格式化输出 -->
        <encoder>
            <pattern>${PATTERN}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
  
  
    <logger name="okhttp3.OkHttpClient" level="INFO" additivity="false">
        <appender-ref ref="LOG_FILE_OKHTTP"/>
    </logger>
  
    <logger name="org.apache.kafka" level="INFO" additivity="false">
        <appender-ref ref="LOG_FILE_MIDDLEWARE"/>
    </logger>
    <logger name="org.apache.zookeeper" level="INFO" additivity="false">
        <appender-ref ref="LOG_FILE_MIDDLEWARE"/>
    </logger>
    <logger name="org.apache.curator" level="INFO" additivity="false">
        <appender-ref ref="LOG_FILE_MIDDLEWARE"/>
    </logger>
  
    <!--    prod 和 pre 进行异步日志-->
    <springProfile name="prod,pre">
        <appender name="ASYNC_APPLICATION" class="ch.qos.logback.classic.AsyncAppender">
            <neverBlock>true</neverBlock>
            <queueSize>5120</queueSize>
            <discardingThreshold>20</discardingThreshold>
            <includeCallerData>true</includeCallerData>
            <appender-ref ref="APPLICATION"/>
        </appender>
        <appender name="ASYNC_ERROR" class="ch.qos.logback.classic.AsyncAppender">
            <neverBlock>true</neverBlock>
            <queueSize>5120</queueSize>
            <discardingThreshold>20</discardingThreshold>
            <includeCallerData>true</includeCallerData>
            <appender-ref ref="FILE_ERROR"/>
        </appender>
        <appender name="ASYNC_LOG_FILE_MIDDLEWARE" class="ch.qos.logback.classic.AsyncAppender">
            <neverBlock>true</neverBlock>
            <queueSize>1024</queueSize>
            <discardingThreshold>20</discardingThreshold>
            <includeCallerData>false</includeCallerData>
            <appender-ref ref="LOG_FILE_MIDDLEWARE"/>
        </appender>
        <appender name="ASYNC_LOG_FILE_OKHTTP" class="ch.qos.logback.classic.AsyncAppender">
            <queueSize>1024</queueSize>
            <discardingThreshold>20</discardingThreshold>
            <neverBlock>true</neverBlock>
            <includeCallerData>false</includeCallerData>
            <appender-ref ref="LOG_FILE_OKHTTP"/>
        </appender>
        <root level="INFO">
            <appender-ref ref="ASYNC_APPLICATION"/>
            <appender-ref ref="ASYNC_ERROR"/>
        </root>
    </springProfile>
  
    <!--test 环境和本地 不进行异步日志-->
    <springProfile name="!prod,!pre">
        <root level="INFO">
            <appender-ref ref="APPLICATION"/>
            <appender-ref ref="FILE_ERROR"/>
            <!--本地环境打印console 日志-->
            <springProfile name="!test,!dev">
                <appender-ref ref="CONSOLE"/>
            </springProfile>
        </root>
    </springProfile>
  
    <!-- 日志通过系统环境变量配置spring active: export SPRING_PROFILES_ACTIVE=dev
     https://blog.csdn.net/weixin_42033269/article/details/102805546 -->
  
</configuration>
相关推荐
robin_suli20 分钟前
Java多线程八股(三)一>多线程环境使用哈希表和ArrayList
java·开发语言·多线程·哈希表
NiNg_1_23424 分钟前
Java中的多线程
java·开发语言
丁总学Java31 分钟前
nohup java -jar supporterSys.jar --spring.profiles.active=prod &
java·spring·jar
呆呆小雅33 分钟前
C# 结构体
android·java·c#
谢尔登33 分钟前
使用 Maven 创建 jar / war 项目
java·maven·jar
理想不理想v1 小时前
前端开发工程师需要学什么?
java·前端·vue.js·webpack·node.js
赶路人儿1 小时前
IntelliJ IDEA配置(mac版本)
java·macos·intellij-idea
jjw_zyfx1 小时前
docker 的各种操作
java·docker·eureka
生财1 小时前
获取字 short WORD 上指定的位是否有效
java·服务器·c#
hummhumm1 小时前
第 36 章 - Go语言 服务网格
java·运维·前端·后端·python·golang·java-ee