概念阐述

ServiceComb提供了基于Vert.x的access log功能。当用户使用REST over Vertx通信方式时,可以通过简单的配置启用access log打印功能。

场景描述

用户在调试服务时可能需要开启access log。在使用REST over servlet通信方式的情况下,可以使用web容器的access log功能;而在使用REST over Vertx通信方式的情况下,可以使用ServiceComb提供的一套access log功能。

配置说明

启用Access Log

用户需要在microservice.yaml文件中增加配置以启用access log,配置示例如下:

  1. servicecomb:
  2. accesslog:
  3. enabled: true ## 启用access log

Access log 配置项说明

配置项取值范围默认值说明
servicecomb.accesslog.enabledtrue/falsefalse如果为true则启用access log,否则不启用
servicecomb.accesslog.pattern表示打印格式的字符串"%h - - %t %r %s %B"配置项见日志元素说明表
说明:- 以上两个配置项均可省略,若省略则使用默认值。

日志格式配置

目前可用的日志元素配置项见 日志元素说明表(Apache & W3C)日志元素说明表(ServiceComb)

日志元素说明表 (Apache & W3C)

元素名称Apache日志格式W3C日志格式说明
HTTP method%mcs-method-
HTTP status%ssc-status-
Duration in second%T--
Duration in millisecond%D--
Remote hostname%h--
Local hostname%v--
Local port%p--
Size of response%B-如果消息体长度为零则打印"0"
Size of response%b-如果消息体长度为零则打印"-"
First line of request%r-包含HTTP Method、Uri、Http版本三部分内容
URI path%Ucs-uri-stem-
Query string%qcs-uri-query-
URI path and query string-cs-uri-
Request protocol%H--
Datetime the request is received%t-按照默认设置打印时间戳,格式为"EEE, dd MMM yyyy HH:mm:ss zzz",语言为英文,时区为GMT
Configurable datetime the request is received%{PATTERN}t-按照指定的格式打印时间戳,语言为英文,时区为GMT
Configurable datetime the request is received%{PATTERN|TIMEZONE|LOCALE}t-按照指定的格式、语言、时区打印时间戳。允许省略其中的某部分配置(但两个分隔符号"|"不可省略)。
Request header%{VARNAME}i-如果没有找到指定的header,则打印"-"
Response header%{VARNAME}o-如果没有找到指定的header,则打印"-"
Cookie%{VARNAME}C-如果没有找到指定的cookie,则打印"-"

日志元素说明表(ServiceComb)

ElementPlaceholderComment
TraceId%SCB-traceId打印ServiceComb生成的trace id,找不到则打印"-"
Invocation Context%{VARNAME}SCB-ctx打印key为VARNAME的invocation context值,找不到则打印"-"

日志输出文件配置

Access log的日志打印实现框架默认采用Log4j,并提供了一套默认的日志文件配置。用户可以在自己定义的log4j.properties文件中覆写这些配置。用户可配置的日志文件配置项见下表。

日志文件配置项

配置项默认值含义说明
paas.logs.accesslog.dir${paas.logs.dir}日志文件输出目录与普通日志输出到同一个目录中
paas.logs.accesslog.fileaccess.log日志文件名-
log4j.appender.access.MaxBackupIndex10最大保存的日志滚动文件个数-
log4j.appender.access.MaxFileSize20MB日志文件最大体积正在记录的文件达到此大小时触发日志滚动存储
log4j.appender.access.logPermissionrw———-日志文件权限-
注意:由于ServiceComb的日志打印功能只依赖slf4j的接口,因此用户可以选择其他日志打印框架,选择其他日志打印框架时需要用户自行配置日志文件输出选项。

日志实现框架切换为logback

针对采用logback作为日志打印框架的项目,需要将日志打印框架依赖从Log4j改为logback并添加部分配置以使access log功能正常生效。

1. 排除Log4j依赖

在将日志实现框架切换为logback之前,需要检查项目的依赖,从中排除掉Log4j相关的依赖项。在项目中运行maven命令dependency:tree,找出其中依赖了Log4j的ServiceComb组件,在其<dependency>依赖项中添加如下配置:

  1. <exclusion>
  2. <groupId>org.slf4j</groupId>
  3. <artifactId>slf4j-log4j12</artifactId>
  4. </exclusion>

2. 添加logback依赖

在pom文件中添加logback的依赖项:

  1. <dependency>
  2. <groupId>org.slf4j</groupId>
  3. <artifactId>slf4j-api</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>ch.qos.logback</groupId>
  7. <artifactId>logback-classic</artifactId>
  8. </dependency>
  9. <dependency>
  10. <groupId>ch.qos.logback</groupId>
  11. <artifactId>logback-core</artifactId>
  12. </dependency>

3. 配置access log组件的logger

由于ServiceComb提供的日志打印组件是获取名为accesslog的logger来打印access log的,因此将日志实现框架从Log4j切换为logback的关键就是提供一个名为accesslog,并为其配置好日志输出文件。以下是access log在logback配置文件中的配置示例(本示例仅展示access log相关的配置,其他日志配置均省略):

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3. <!-- 用户可根据需要自定义appender -->
  4. <appender name="ACCESSLOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
  5. <file>./logs/access.log</file>
  6. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  7. <fileNamePattern>./logs/access-%d{yyyy-MM-dd}.log</fileNamePattern>
  8. </rollingPolicy>
  9. <!-- 注意:由于access log的内容是在代码中完成格式化的,因此这里只需输出message即可,无需添加额外的格式 -->
  10. <encoder>
  11. <pattern>%msg%n</pattern>
  12. </encoder>
  13. </appender>
  14. <!-- 提供一个名为"accesslog"的logger供access log打印组件使用 -->
  15. <logger name="accesslog" level="INFO" additivity="false">
  16. <appender-ref ref="ACCESSLOG" />
  17. </logger>
  18. </configuration>

自定义扩展Access Log

用户可以利用ServiceComb提供的AccessLogItem扩展机制,定制自己的AccessLogItem。

相关类说明

  • AccessLogItem
  1. public interface AccessLogItem<T> {
  2. /**
  3. * 从accessLogParam中获取特定的内容,组装成access log的打印内容并返回
  4. */
  5. String getFormattedItem(AccessLogParam<T> accessLogParam);
  6. }

AccessLogItem的定义如上所示,每一次请求触发Access Log打印时,ServiceComb的Access Log机制都会遍历有效的AccessLogItem,调用getFormattedItem方法获取此Item生成的Access Log片段,并将全部片段拼接成一条Access Log打印到日志文件中。参数AccessLogParam<T>包含请求开始时间、结束时间以及类型为T的请求上下文信息,在REST over Vertx通信方式中,类型T为Vert.x的RoutingContext

  • VertxRestAccessLogItemMeta
  1. // pattern占位符前缀
  2. protected String prefix;
  3. // pattern占位符后缀
  4. protected String suffix;
  5. // 优先级序号
  6. protected int order;
  7. // AccessLogItem构造器
  8. protected AccessLogItemCreator<RoutingContext> accessLogItemCreator;

VertxRestAccessLogItemMeta包含如上属性,它定义了ServiceComb如何解析pattern字符串以获得特定的AccessLogItem。

  • 如果用户想要定义一个占位符为%user-definedAccessLogItem,则需要声明一个VertxRestAccessLogItemMeta的子类,设置prefix="%user-defined",suffix=null,当AccessLogPatternParser解析到"%user-defined"时,从此meta类中取得AccessLogItemCreator创建对应的AccessLogItem注意:由于"%user-defined"占位符中没有变量部分,因此调用AccessLogItemCreator传入的配置参数为null。
  • 如果用户想要定义一个占位符为%{VARNAME}user-definedAccessLogItem,则声明的VertxRestAccessLogItemMeta子类中,设置prefix="%{",suffix="}user-defined",当AccessLogPatternParser解析到"%{VARNAME}user-defined"时,会截取出"VARNAME"作为配置参数传入AccessLogItemCreator,创建一个AccessLogItem
    VertxRestAccessLogItemMeta有一个子类CompositeVertxRestAccessLogItemMeta,当用户需要定义多个AccessLogItem时,可以将多个VertxRestAccessLogItemMeta聚合到CompositeVertxRestAccessLogItemMeta中。Parser加载到类型为CompositeVertxRestAccessLogItemMeta的AccessLogItemMeta时,会调用其getAccessLogItemMetas()方法获得一组AccessLogItemMeta。VertxRestAccessLogItemMeta使用SPI机制加载,而CompositeVertxRestAccessLogItemMeta可以让用户只在SPI配置文件中配置一条记录就加载多条meta信息,给了用户更灵活的选择。
  • AccessLogItemCreator
  1. public interface AccessLogItemCreator<T> {
  2. // 接收配置值,返回一个AccessLogItem。如果AccessLogItem的占位符没有可变的配置值部分,则此方法会接收到null。
  3. AccessLogItem<T> createItem(String config);
  4. }

用户通过设置在自定义的VertxRestAccessLogItemMeta中的AccessLogItemCreator实例化自己的AccessLogItem。由于这是一个函数式接口,当AccessLogItem的初始化方式较简单时,可以直接使用Lambda表达式定义Creator,以简化开发。

AccessLogItemMeta的匹配规则

AccessLogItemMeta加载进Parser后,会进行一次排序。Parser解析pattern串时会从前到后匹配meta list,总的匹配规则如下:

  • 优先匹配高优先级的meta。
  • 优先匹配有后缀的meta,当匹配上多个有后缀meta时,取前后缀相距最小的一个。
  • 优先匹配占位符长的meta,例如有两个meta,"%abc"和"%a",如果匹配中了"%abc"则直接返回,不再匹配"%a"。

示例说明

  • 扩展自定义AccessLogItem

首先用户需要AccessLogItem接口实现自己的item:

  1. public class UserDefinedAccessLogItem implements AccessLogItem<RoutingContext> {
  2. private String config;
  3. public UserDefinedAccessLogItem(String config) {
  4. this.config = config;
  5. }
  6. @Override
  7. public String getFormattedItem(AccessLogParam<RoutingContext> accessLogParam) {
  8. // 此处是用户自定义的逻辑,需要从AccessLogParam或其他地方取相关数据,生成并返回access log片段
  9. return "user-defined-[" + config + "]-[" + accessLogParam.getStartMillisecond() + "]";
  10. }
  11. }
  • 定义AccessLogItem的meta类

继承VertxRestAccessLogItemMetaCompositeVertxRestAccessLogItemMeta类,定义AccessLogItem的前后缀等信息:

  1. public class UserDefinedCompositeExtendedAccessLogItemMeta extends CompositeVertxRestAccessLogItemMeta {
  2. private static final List<VertxRestAccessLogItemMeta> META_LIST = new ArrayList<>();
  3. static {
  4. META_LIST.add(new VertxRestAccessLogItemMeta("%{", "}user-defined", UserDefinedAccessLogItem::new));
  5. }
  6. @Override
  7. public List<VertxRestAccessLogItemMeta> getAccessLogItemMetas() {
  8. return META_LIST;
  9. }
  10. }
  • 配置SPI加载文件

resources/META-INF/services/目录下定义一个名为"org.apache.servicecomb.transport.rest.vertx.accesslog.parser.VertxRestAccessLogItemMeta"的文件,将上一步中定义的meta类完整类名填写到该文件中,供Parser加载meta类。

  • 配置Access Log的pattern

在microservice.yaml文件中的配置pattern,假设为"%{test-config}user-defined",运行服务触发Access Log打印,假设请求开始时间为1,则可以看到Access Log打印内容为"user-defined-[test-config]-[1]"。

示例代码

microservice.yaml文件中的配置

  1. ## other configurations omitted
  2. servicecomb:
  3. accesslog:
  4. enabled: true ## 启用access log
  5. pattern: "%h - - %t %r %s %B" ## 自定义日志格式

log4j.properties文件中的配置

  1. # access log configuration item
  2. paas.logs.accesslog.dir=../logs/
  3. paas.logs.accesslog.file=access.log
  4. # access log File appender
  5. log4j.appender.access.MaxBackupIndex=10
  6. log4j.appender.access.MaxFileSize=20MB
  7. log4j.appender.access.logPermission=rw-------