日志
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
spring.profiles.active=dev
- 系统参数
export SPRING_PROFILES_ACTIVE=dev
- 命令行参数
java -jar your-application.jar --spring.profiles.active=prod
通过配置中心注入系统环境或者属性进行配置(放在公共配置,多应用统一使用),或者统一运维的部署脚本中指定,通过标准化的路径实现环境标准化,无需自定义。
环境隔离日志配置
这个是 logback-spring.xml 里面的配置
非生产、非预发、非测试 非开发环境打印console
springProfile 灵活的配置,可以让开发处理多环境的配置信息非常easy
<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 基础上包装一个异步日志 ,如下配置。
<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环境隔离即可完成,如下配置。
<!-- 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 也行?
查看一下异步日志的实例是否存在
[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__126">logback 配置文件不推荐放在配置中心
配置文件和源码放在一起,通过环境变量区分不同环境的处理方式比较好,方便修改
某些场景需要隐藏某些日志,放在配置中心非常不方便,修改起来也非常麻烦,修改错误了也不知道,只有发布的时候才能感知。
eg: kafka的日志不要放在all 日志里面,需要在线日志系统订阅关键的业务日志信息,减少费用。
<logger name="org.apache.kafka" level="INFO" additivity="false"><appender-ref ref="LOG_FILE_MIDDLEWARE"/>
</logger>
eg: 我要禁用日志 ,这种想法可能跟随者业务需求进行变化。
<logger name="org.redisson.connection.DNSMonitor" level="OFF"/>
分离日志
- additivity=“false” 不加入总日志信息
- error 日志分离
<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 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=devhttps://blog.csdn.net/weixin_42033269/article/details/102805546 --></configuration>