背景
目前在项目处理日志层面上,每个项目都需要引入一个logback-spring.xml文件,而且在该文件中,内容很长,很多一摸一样的东西,而且各个项目格式不同,从而对项目排查带来很大的困扰,所以目前想解决的问题如下:
- 对于没有埋点日志的项目,不需要引入xml文件;
- 对于需要埋点日志的项目,尽可能做到精简;
- 统一格式(后续如果推送到skywalking、es等等日志分析工具的时候,这里统一的格式处理起来会比较方便点);
- 方便维护,统一管理。
实践
整体分为如下部分:
- 定义一个公共starter包,各个项目必须引用;
- 定义xml文件,主要实现系统日志生成,默认系统全局变量(比如统一系统日志格式、埋点格式、以及一些必要字段添加)、默认日志输出
- 项目如何使用
公共starter
该starter名字为commons-starter
pom引用
项目需要支持springboot web功能,因为springboot 项目已经默认集成了logback,需要在这里我直接引用了springwebstarter(也可以单独只引用logback相关ajr包)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.12.RELEASE</version>
<scope>compile</scope>
</dependency>
在resources中定义两个文件:
-
logback-base.xml
定义全局公共部分,包括系统日志appender、全局日志系统变量等等
-
logback-spring.xml
作为项目默认日志输出,可以让一些没有特殊定制化需求的项目,无需引入logback-spring.xml文件
工程目录如下:
xml文件
因为我这里需要打印skywalking的traceId,需要引用apm-toolkit-logback-1.x的包
并且,我在logback中使用了<if> 标签,必须引用janino和commons-compiler,该功能才可以实现,如果不需要<if>标签功能,可以不引用这两个包
整体pom如下
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>3.0.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.codehaus.janino/commons-compiler -->
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>commons-compiler</artifactId>
<version>3.0.8</version>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>${skywalking.logback.version}</version>
</dependency>
logback-base.xml
<included>
<conversionRule conversionWord="SW_CTX"
converterClass="org.apache.skywalking.apm.toolkit.log.logback.v1.x.LogbackSkyWalkingContextPatternConverter" />
<conversionRule conversionWord="TID"
converterClass="org.apache.skywalking.apm.toolkit.log.logback.v1.x.LogbackPatternConverter" />
<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="%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %boldMagenta([%SW_CTX]) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
<springProperty scope="context" name="springApplicationName" source="spring.application.name"
defaultValue="unknown" />
<springProperty scope="context" name="appenv" source="spring.profiles.active"
defaultValue="test" />
<property name="logApp" value="${springApplicationName}-${appenv}"/>
<property name="logAppSys" value="${logApp}-sys"/>
<property name="logAppBiz" value="${logApp}-biz"/>
<if condition='property("appenv").equals("prod")'>
<then>
<property name="systemEnv" value="standend" />
</then>
<else>
<property name="systemEnv" value="dev"/>
</else>
</if>
<!--统一系统日志输出格式-->
<property name="sysLogParttern" value='{"timestamp": "%d{yyyy-MM-dd HH:mm:ss.SSS}","fb_collect_app":"${logAppSys}","system_env":"${systemEnv}","traceId": "[%TID]","traceCtx":"[%SW_CTX]","level": "%level","service": "${springApplicationName}", "thread": "%thread", "class": "%logger{50}", "msg": "%msg", "exception":"%exception{10}" }%n'/>
<!--统一埋点日志输出格式-->
<!--注意看,我这里的埋点日志是msg字段,我需要在埋点日志多增加一些额外的字段,比如时间,系统环境,trace ID等等,埋点日志只需要埋相关的业务字段-->
<property name="bizLogParttern" value='{"business_time": "%d{yyyy-MM-dd HH:mm:ss.SSS}","fb_collect_app":"${logAppBiz}","system_env":"${systemEnv}","traceId": "[%TID]","traceCtx":"[%SW_CTX]","msg": %msg }%n'/>
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 系统文件输出 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/tmp/logs/sys/${springApplicationName}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件路径 -->
<fileNamePattern>/tmp/logs/sys/${springApplicationName}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 保留的历史日志文件数量 -->
<maxHistory>7</maxHistory>
<!-- 每个日志文件的最大大小 -->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>${sysLogParttern}</pattern>
</encoder>
</appender>
<!--开启logback异步记录日志-->
<appender name="sysAsync" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE" />
</appender>
<!-- 使用gRpc将日志发送到skywalking服务端 -->
<appender name="grpc-skywalking" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<Pattern>${sysLogParttern}</Pattern>
</layout>
</encoder>
</appender>
<!-- 日志级别 -->
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="sysAsync"/>
<appender-ref ref="grpc-skywalking"/>
</root>
</included>
上述功能,如果不需要使用skywalking相关功能点,需要修改这几个功能:
去掉<conversionRule> 标签 SW_CTX、TID;
<conversionRule conversionWord="SW_CTX" converterClass="org.apache.skywalking.apm.toolkit.log.logback.v1.x.LogbackSkyWalkingContextPatternConverter" /> <conversionRule conversionWord="TID" converterClass="org.apache.skywalking.apm.toolkit.log.logback.v1.x.LogbackPatternConverter" />
去掉
CONSOLE_LOG_PATTERN
中的%boldMagenta([%SW_CTX])
去掉
sysLogParttern
、bizLogParttern
中的 “traceId”: “[%TID]”,“traceCtx”:"[%SW_CTX]"即可去掉如下配置:
<!-- 使用gRpc将日志发送到skywalking服务端 --> <appender name="grpc-skywalking" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender"> <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout"> <Pattern>${sysLogParttern}</Pattern> </layout> </encoder> </appender>
logback-spring.xml
该文件其实是非必须,如果你想项目不需要集成xml文件就实现日志输出功能,那么该文件就可以考虑放到公共starter中
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="logback-base.xml"/>
</configuration>
项目使用
默认功能
如果没有埋点日志,只需要实现系统埋点日志功能,那么直接引入pom文件即可;
<dependency>
<groupId>com.commons.starter</groupId>
<artifactId>commons-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
埋点日志需求
如果业务需要额外的日志输出,按如下步骤实现:
- 项目新建logback-spring.xml文件,并引入logback-base.xml文件
- xml文件新增业务appender;
- 日志输出定义;
示例如下:
logbackj-spring.xml
<configuration>
<!-- 这个是必须,一定要注意!!! -->
<include resource="logback-base.xml"/>
<!-- 埋点输出 -->
<appender name="bizlogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/tmp/logs/biz/${springApplicationName}-biz.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件路径 -->
<fileNamePattern>/tmp/logs/biz/${springApplicationName}.%d{yyyy-MM-dd}.%i-biz.log</fileNamePattern>
<!-- 保留的历史日志文件数量 -->
<maxHistory>7</maxHistory>
<!-- 每个日志文件的最大大小 -->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>${bizLogParttern}</pattern>
</encoder>
</appender>
<!-- 添加信息 -->
<logger name="bizlog" additivity="false" level="INFO">
<appender-ref ref="bizlogAppender"/>
</logger>
</configuration>
Java如何使用
在Java类使用的时候,如下:
注意:LoggerFactory.getLogger(“bizlog”)中的bizlog来自于你在logback.xml文件中定义的<logger>
class test{
//bizlog来自于你在logback.xml文件中定义的<logger>
private static final Logger custom = LoggerFactory.getLogger("bizlog");
public void aa(){
// 后面是你自己跟业务约定的json体
custom.info("{\"timestamp\":\"2024-06-28T12:00:00Z\",\"loglevel\":\"INFO444\",\"logs\":\"User logged in\"}");
}
}
总结
上面主要讲的是一个公共starter解决多个项目的公共配置,这么做主要是为了统一的日志格式,日志公共处理,这么做可以为后续的日志采集,日志统计分析做铺垫,比如项目集成filebeat、logstash的话,对于日志清洗,筛选,大大简化了很多复杂的东西。
主要还是提供一个思路,大家可以参考,不一定完全按照这个来实现
拓展点
logback中的logger和root区别
logger和root logger都是日志记录器(logger)的实例,用于控制日志的输出级别。它们之间的区别在于它们的作用范围和默认配置。
logger是应用程序中的具体组件或类的日志记录器。每个logger都可以单独配置,以控制特定组件或类的日志输出级别。通过logger,我们可以对不同的组件或类设置不同的日志级别,从而灵活地控制日志的输出。
root logger是logger的顶级父级别。它是logger层次结构的根节点,用于控制整个应用程序的日志输出级别。如果没有为特定的logger配置日志级别,那么它将继承root logger的日志级别。因此,root logger的日志级别可以被视为默认的全局日志级别。
示例
<!-- 系统模块日志级别控制 -->
<logger name="com.demo" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
上面的配置代码是一个典型的日志配置文件,用于控制日志输出的级别。在这个配置中,有两个地方设置了日志级别:logger 元素和 root 元素。
-
logger 元素:logger 元素用于指定特定包或类的日志级别。在上面的配置中,com.demo 包的日志级别被设置为 info,org.springframework 包的日志级别被设置为 warn。这意味着在这两个包中的日志输出将受到相应级别的限制。更具体地说,info 级别将输出 info、warn 和 error 级别的日志,而 warn 级别将输出 warn 和 error 级别的日志。
-
root 元素:root 元素用于设置默认的日志级别,适用于所有未被特定 logger 元素覆盖的包或类。在上面的配置中,root 元素的日志级别被设置为 info。这意味着除了被特定 logger 元素设置为其他级别的包或类外,所有其他包或类的日志输出将受到 info 级别的限制。
总结起来,logger 元素用于设置特定包或类的日志级别,而 root 元素用于设置默认的日志级别。如果特定包或类的日志级别被设置为某个级别,那么该级别及其以上级别的日志将被输出。如果未被特定设置的包或类将受到默认的日志级别限制。在上面的配置中,com.demo 包的日志级别为 info,org.springframework 包的日志级别为 warn,而其他包或类的日志级别将受到默认的 info 级别限制。