运维问题排查能力提升专项-02-logback使用实战

    技术2022-07-10  128

    一、前言

    logback是一款优秀的日志框架,他本身的功能很强大,且公司的绝大部分java应用都集成了这个框架并使用其管理应用日志(最新的技术规范中也明确需要使用该框架管理日志)。但考虑到有部分应用未能合理配置框架参数,导致应用未能合理记录日志或因不合理配置导致应用性能受到影响等问题,特此分享一下正常情况下应如何使用logback管理日志,以及目前已知容易出现的问题和对应解决方案。

    二、主体

    2.1、组件集成

    因为各个项目内部不一定如何引用的组件类,所以不推荐现场自行研究组件集成,一个不小心就会导致项目无法启动! 默认lib及classes存放路径会根据是否springboot项目而不同 普通war的存放路径一般为xxx.war\WEB-INF\,springboot的存放路径一般为xxx.jar\BOOT-INF\

    2.1.1、确认当前项目是否集成logback

    进入lib目录确认是否有slf4j-api-版本号.jar及logback-classic-版本号.jar、logback-core-版本号.jar三个jar包进入classes目录确认是否有logback.xml、logback-test.xml或logback-spring.xml三个配置文件中的任意一个

    2.1.2、确认当前项目是否集成log4jdbc

    确认数据源配置的驱动类driver-class-name(有的是driverClass)为net.sf.log4jdbc.sql.jdbcapi.DriverSpy(必须,如果不是代表没集成)确认数据源配置的url(有的是jdbc-url)是以jdbc:log4jdbc开头的(必须,如果不是则集成后也不会生效,例如jdbc:log4jdbc:postgresql://172.16.XX.XX:XXXX/db_XXXXX?currentSchema=db_xxx)确认存在log4jdbc.log4j2.properties配置(springboot推荐直接将该配置使用yml管理,不要创建该文件)确认log4jdbc.log4j2.properties中正确配置了项目实际使用的驱动类(drivers属性为实际驱动类配置项,若多数据源且类型不一致需配置多个,英文逗号分隔)

    2.2、logback推荐配置

    <?xml version="1.0" encoding="UTF-8"?> <!-- scan是否扫描配置文件变更,若为真则会定时扫描本地配置文件变更并更新对应配置;scanPeriod定时扫描间隔;debug是否输出logback框架内部的调试日志 --> <configuration scan="true" scanPeriod="60 second" debug="false"> <!-- define动态获取LOG_HOME, TAS为${TAS_HOME}/logs, tomcat为${CATALINA_HOME}/logs, springboot为相对于jar路径的logs目录, 如需其他路径可自行配置property--> <!--<property name="LOG_HOME" value="/opt/thunisoft/logs"/>--> <!-- 下面这三个配置LOG_HOME、IP、PORT是thunisoft-logback-版本号.jar中自带的,也可以直接写死 --> <define name="LOG_HOME" class="com.thunisoft.logback.LogbackHomeGetProperty"/> <define name="IP" class="com.thunisoft.logback.LogbackIpGetProperty"/> <define name="PORT" class="com.thunisoft.logback.LogbackPortGetProperty"/> <!-- springboot的level在application.yml中配置,且必须用springProperty配置;若非springboot项目请使用property标签 --> <springProperty scope="context" name="LOG_LEVEL_ROOT" source="logback.level.root" defaultValue="error"/> <springProperty scope="context" name="LOG_LEVEL_COMMON" source="logback.level.common" defaultValue="error"/> <springProperty scope="context" name="APP_NAME" source="server.name" defaultValue="appName"/> <statusListener class="ch.qos.logback.core.status.NopStatusListener"/> <!-- 控制台输出(控制台输出是加锁的,所以大量的日志输出会出现锁竞争,影响运行效率,生产环境不允许长期大量日志输出到控制台) --> <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <Target>System.out</Target> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} [%-5level] [%-5thread] %logger{20} - %msg%n</pattern> </encoder> </appender> <!-- 普通日志输出文件 --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <File>${LOG_HOME}/${APP_NAME}_stdout.log</File> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>${LOG_HOME}/${APP_NAME}_stdout.%d{yyyy-MM-dd}.log</FileNamePattern> <!-- 最大日志保留天数,可以根据现场实际需要调整 --> <maxHistory>20</maxHistory> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>%d{yyyy-MM-dd HH:mm:ss} [%-5level] [%-5thread] %logger{20} - %msg%n</pattern> </encoder> </appender> <!-- 错误日志输出文件 --> <appender name="FILE-ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>error</level> </filter> <File>${LOG_HOME}/${APP_NAME}_stderr.log</File> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>${LOG_HOME}/${APP_NAME}_stderr.%d{yyyy-MM-dd}.log</FileNamePattern> <maxHistory>20</maxHistory> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>%d{yyyy-MM-dd HH:mm:ss} [%-5level] [%-5thread] %logger{20} - %msg%n</pattern> </encoder> </appender> <!-- 默认jdbc日志输出文件 --> <appender name="FILE-JDBC" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 集群部署日志名建议加上ip和port --> <File>${LOG_HOME}/${APP_NAME}_jdbc_${IP}_${PORT}.log</File> <encoder> <!-- log4jdbc的日志输出格式不推荐修改,否则可能影响后续sqlfx工具进行内容识别 --> <pattern>%d{yyyy-MM-dd HH:mm:ss} [%-5level] [%-5thread] [%X{request.url}] %logger{20} - %msg%n</pattern> </encoder> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>${LOG_LEVEL_COMMON}</level> </filter> <append>true</append> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_HOME}/${APP_NAME}_jdbc.%d{yyyy-MM-dd}_${IP}_${PORT}.log</fileNamePattern> <maxHistory>7</maxHistory> </rollingPolicy> </appender> <!-- 关闭不常用日志输出 BEGIN --> <!-- UIM缓存刷新日志,平常用不到,需要定位问题时再打开 --> <logger name="com.thunisoft.aap.uim.client.organ.ehcache" level="off" /> <!-- druid线程池相关日志(若使用的其他线程池组件,请更换包路径) --> <logger name="com.alibaba.druid.pool" level="off" /> <!-- 关闭不常用日志输出 END --> <!-- log4jdbc相关配置 BEGIN --> <!-- jdbc相关无用输出 --> <logger name="jdbc.connection" level="off" /> <logger name="jdbc.audit" level="off" /> <logger name="jdbc.resultset" level="off" /> <logger name="jdbc.resultsettable" level="off" /> <!-- 执行脚本前输出日志-不包含执行时间(sql执行失败时jdbc.sqltiming是不会输出的,可以使用jdbc.sqlonly来控制脚本输出。他俩一个是在执行sql前输出,一个是在执行sql后输出) --> <!-- 执行脚本前输出日志-不包含执行时间(sql执行失败时jdbc.sqltiming是不会输出的,可以使用jdbc.sqlonly来控制脚本输出。他俩一个是在执行sql前输出,一个是在执行sql后输出) --> <!-- 执行脚本前输出日志-不包含执行时间(sql执行失败时jdbc.sqltiming是不会输出的,可以使用jdbc.sqlonly来控制脚本输出。他俩一个是在执行sql前输出,一个是在执行sql后输出) --> <logger name="jdbc.sqlonly" level="off" /> <!-- <logger name="jdbc.sqlonly" level="${LOG_LEVEL_COMMON}" additivity="false"> <appender-ref ref="FILE-JDBC" /> </logger> --> <!-- 执行脚本后输出日志-包含执行时间 --> <logger name="jdbc.sqltiming" level="${LOG_LEVEL_COMMON}" additivity="false"> <!-- 调试程序时可以打开该选项,同步输出脚本信息到控制台,正式环境不要启用,影响效率 --> <!-- <appender-ref ref="stdout"/> --> <appender-ref ref="FILE-JDBC"/> </logger> <!-- log4jdbc相关配置 END --> <!-- 各应用通用配置 BEGIN --> <!-- 推荐各应用根路径单独配置,且不输出到控制台,保留root输出到控制台的功能,否则不好监控应用是否正常启动 --> <!-- ${LOG_LEVEL_COMMON} 是在上面定义的变量,可根据现场实际情况修改或写死 --> <logger name="com.thunisoft" level="${LOG_LEVEL_COMMON}" additivity="false"> <!-- 调试程序时可以打开该选项,同步输出脚本信息到控制台,正式环境不要启用,影响效率 --> <!-- <appender-ref ref="stdout"/> --> <appender-ref ref="FILE"/> <appender-ref ref="FILE-ERROR"/> </logger> <!-- root节点配置不推荐修改,保持默认即可 --> <!-- ${LOG_LEVEL_COMMON} 是在上面定义的变量,可根据现场实际情况修改或写死 --> <root level="${LOG_LEVEL_ROOT}"> <!-- 默认所有日志均输出至控制台(若针对指定包单独配置了logger,且对应additivity属性为false则会覆盖root的配置,所以也不会有大量日志输出到控制台) --> <appender-ref ref="stdout"/> <!-- 所有日志均输出至文件中,便于追溯历史(注意一般都有配置maxHistory,只会保留最近一段时间的日志。另外现场必须配置maxHistory,之前发现现场有留存几年的日志文件) --> <appender-ref ref="FILE"/> <!-- 错误级别的日志单独输出到指定文件,便于更专注的排查问题 --> <appender-ref ref="FILE-ERROR"/> </root> <!-- 各应用通用配置 END --> </configuration>

    2.3、注意事项

    不要输出大量的日志到控制台(影响性能)现场不要配置logback-test.xml(不要有多套配置,否则可能导致修改后不生效–主要是没修改对)log4jdbc的日志要单独管理(且可通过灵活配置jdbc.sqltiming及jdbc.sqlonly进行sql问题定位)

    三、参考资料

    Java 日志实现框架 Logback https://juejin.im/post/5e0303bc6fb9a01637709416

    看完这个不会配置 logback ,请你吃瓜! https://juejin.im/post/5b51f85c5188251af91a7525

    Logback配置使用 https://www.jianshu.com/p/638b4e2c4068

    log4jdbc-log4j2浅析(sqlfx客户端采集日志的来源) https://blog.csdn.net/leandzgc/article/details/103035427

    Processed: 0.020, SQL: 9