You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by GitBox <gi...@apache.org> on 2018/06/05 07:29:35 UTC

[GitHub] wujimin closed pull request #734: [SCB-616] Make access log mechanism extensible

wujimin closed pull request #734: [SCB-616] Make access log mechanism extensible
URL: https://github.com/apache/incubator-servicecomb-java-chassis/pull/734
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java
index 2075d15bf..f2ac2fb15 100644
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java
@@ -29,7 +29,6 @@
 import org.apache.servicecomb.foundation.vertx.VertxTLSBuilder;
 import org.apache.servicecomb.transport.rest.vertx.accesslog.AccessLogConfiguration;
 import org.apache.servicecomb.transport.rest.vertx.accesslog.impl.AccessLogHandler;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.impl.DefaultAccessLogPatternParser;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -86,15 +85,15 @@ private void mountAccessLogHandler(Router mainRouter) {
       LOGGER.info("access log enabled, pattern = {}", pattern);
       mainRouter.route()
           .handler(new AccessLogHandler(
-              pattern,
-              new DefaultAccessLogPatternParser()));
+              pattern
+          ));
     }
   }
 
   private void initDispatcher(Router mainRouter) {
     List<VertxHttpDispatcher> dispatchers = SPIServiceUtils.getSortedService(VertxHttpDispatcher.class);
     for (VertxHttpDispatcher dispatcher : dispatchers) {
-      if(dispatcher.enabled()) {
+      if (dispatcher.enabled()) {
         dispatcher.init(mainRouter);
       }
     }
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/AccessLogGenerator.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/AccessLogGenerator.java
index c44491e56..a2079ebf0 100644
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/AccessLogGenerator.java
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/AccessLogGenerator.java
@@ -20,9 +20,8 @@
 import java.util.List;
 
 import org.apache.servicecomb.transport.rest.vertx.accesslog.element.AccessLogItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.AccessLogItemFactory;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemLocation;
 import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogPatternParser;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.impl.VertxRestAccessLogPatternParser;
 
 import io.vertx.ext.web.RoutingContext;
 
@@ -37,15 +36,12 @@
    */
   private AccessLogItem<RoutingContext>[] accessLogItems;
 
-  private AccessLogItemFactory accessLogItemFactory = new AccessLogItemFactory();
+  private AccessLogPatternParser<RoutingContext> accessLogPatternParser = new VertxRestAccessLogPatternParser();
 
   @SuppressWarnings("unchecked")
-  public AccessLogGenerator(String rawPattern, AccessLogPatternParser accessLogPatternParser) {
-    List<AccessLogItemLocation> locationList = accessLogPatternParser.parsePattern(rawPattern);
-
-    List<AccessLogItem<RoutingContext>> itemList = accessLogItemFactory.createAccessLogItem(rawPattern, locationList);
-    accessLogItems = new AccessLogItem[itemList.size()];
-    accessLogItems = itemList.toArray(accessLogItems);
+  public AccessLogGenerator(String rawPattern) {
+    List<AccessLogItem<RoutingContext>> accessLogItemList = accessLogPatternParser.parsePattern(rawPattern);
+    accessLogItems = accessLogItemList.toArray(new AccessLogItem[0]);
   }
 
   public String generateLog(AccessLogParam<RoutingContext> accessLogParam) {
@@ -60,7 +56,6 @@ public String generateLog(AccessLogParam<RoutingContext> accessLogParam) {
     return log.toString();
   }
 
-
   private AccessLogItem<RoutingContext>[] getAccessLogItems() {
     return accessLogItems;
   }
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/AccessLogItemFactory.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/AccessLogItemFactory.java
deleted file mode 100644
index df1c47c9e..000000000
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/AccessLogItemFactory.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.servicecomb.transport.rest.vertx.accesslog.element;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.creator.AccessLogItemCreator;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.creator.PercentagePrefixConfigurableItemCreator;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.creator.SimpleAccessLogItemCreator;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemLocation;
-
-import io.vertx.ext.web.RoutingContext;
-
-/**
- * The factory of {@link AccessLogItem}.
- * Using the {@link AccessLogItemCreator} to generate AccessLogItem, according to {@link AccessLogItemLocation}
- * and rawPattern.
- */
-public class AccessLogItemFactory {
-  private List<AccessLogItemCreator<RoutingContext>> creatorList = Arrays
-      .asList(new SimpleAccessLogItemCreator(), new PercentagePrefixConfigurableItemCreator());
-
-  public List<AccessLogItem<RoutingContext>> createAccessLogItem(String rawPattern,
-      List<AccessLogItemLocation> locationList) {
-    List<AccessLogItem<RoutingContext>> itemList = new ArrayList<>(locationList.size());
-    for (AccessLogItemLocation location : locationList) {
-      setItemList(rawPattern, itemList, location);
-    }
-
-    return itemList;
-  }
-
-  /**
-   * generate single AccessLogItem
-   */
-  private void setItemList(String rawPattern, List<AccessLogItem<RoutingContext>> itemList,
-      AccessLogItemLocation location) {
-    AccessLogItem<RoutingContext> item = null;
-    for (AccessLogItemCreator<RoutingContext> creator : creatorList) {
-      item = creator.create(rawPattern, location);
-      if (null != item) {
-        break;
-      }
-    }
-
-    if (null != item) {
-      itemList.add(item);
-    }
-  }
-}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/creator/PercentagePrefixConfigurableItemCreator.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/creator/PercentagePrefixConfigurableItemCreator.java
deleted file mode 100644
index a22d3b0b4..000000000
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/creator/PercentagePrefixConfigurableItemCreator.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.servicecomb.transport.rest.vertx.accesslog.element.creator;
-
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.AccessLogItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.CookieItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.DatetimeConfigurableItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.InvocationContextItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.PlainTextItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.RequestHeaderItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.ResponseHeaderItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemLocation;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.matcher.PercentagePrefixConfigurableMatcher;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.placeholder.AccessLogItemTypeEnum;
-
-import io.vertx.ext.web.RoutingContext;
-
-/**
- * Some access log item contains changeable part, so we should get it's configuration from rawPattern, and generate it
- * each time it is needed.
- */
-public class PercentagePrefixConfigurableItemCreator implements AccessLogItemCreator<RoutingContext> {
-  @Override
-  public AccessLogItem<RoutingContext> create(String rawPattern, AccessLogItemLocation location) {
-    String config = getConfig(rawPattern, location);
-    switch (location.getPlaceHolder()) {
-      case DATETIME_CONFIGURABLE:
-        return new DatetimeConfigurableItem(config);
-      case REQUEST_HEADER:
-        return new RequestHeaderItem(config);
-      case RESPONSE_HEADER:
-        return new ResponseHeaderItem(config);
-      case COOKIE:
-        return new CookieItem(config);
-      case TEXT_PLAIN:
-        return new PlainTextItem(config);
-      case SCB_INVOCATION_CONTEXT:
-        return new InvocationContextItem(config);
-      default:
-        // unexpected situation
-        return null;
-    }
-  }
-
-  private String getConfig(String rawPattern, AccessLogItemLocation location) {
-    if (location.getPlaceHolder() == AccessLogItemTypeEnum.TEXT_PLAIN) {
-      return rawPattern.substring(location.getStart(), location.getEnd());
-    }
-    return rawPattern.substring(location.getStart() + PercentagePrefixConfigurableMatcher.GENERAL_PREFIX.length(),
-        location.getEnd()
-            - PercentagePrefixConfigurableMatcher.getSuffix(location.getPlaceHolder()).length());
-  }
-}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/creator/SimpleAccessLogItemCreator.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/creator/SimpleAccessLogItemCreator.java
deleted file mode 100644
index 3e4983e25..000000000
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/creator/SimpleAccessLogItemCreator.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.servicecomb.transport.rest.vertx.accesslog.element.creator;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.AccessLogItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.DatetimeConfigurableItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.DurationMillisecondItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.DurationSecondItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.FirstLineOfRequestItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.HttpMethodItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.HttpStatusItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.LocalHostItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.LocalPortItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.QueryStringItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.RemoteHostItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.RequestProtocolItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.ResponseSizeItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.TraceIdItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.UrlPathItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.UrlPathWithQueryItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemLocation;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.placeholder.AccessLogItemTypeEnum;
-
-import io.vertx.ext.web.RoutingContext;
-
-/**
- * For some access log items, their placeholder contains no modifiable part, like "%s" or "sc-status".
- * So we can build a mapping relationship between the placeholder and item instances, when an item is needed, get it by
- * it's placeholder.
- */
-public class SimpleAccessLogItemCreator implements AccessLogItemCreator<RoutingContext> {
-  private static final Map<AccessLogItemTypeEnum, AccessLogItem<RoutingContext>> SIMPLE_ACCESSLOG_ITEM_MAP =
-      new HashMap<>();
-
-  static {
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.HTTP_METHOD, new HttpMethodItem());
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.HTTP_STATUS, new HttpStatusItem());
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.DURATION_IN_SECOND, new DurationSecondItem());
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.DURATION_IN_MILLISECOND, new DurationMillisecondItem());
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.REMOTE_HOSTNAME, new RemoteHostItem());
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.LOCAL_HOSTNAME, new LocalHostItem());
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.LOCAL_PORT, new LocalPortItem());
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.RESPONSE_SIZE, new ResponseSizeItem("0"));
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.RESPONSE_SIZE_CLF, new ResponseSizeItem("-"));
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.FIRST_LINE_OF_REQUEST, new FirstLineOfRequestItem());
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.URL_PATH, new UrlPathItem());
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.QUERY_STRING, new QueryStringItem());
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.URL_PATH_WITH_QUERY, new UrlPathWithQueryItem());
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.REQUEST_PROTOCOL, new RequestProtocolItem());
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.DATETIME_DEFAULT, new DatetimeConfigurableItem());
-    SIMPLE_ACCESSLOG_ITEM_MAP.put(AccessLogItemTypeEnum.SCB_TRACE_ID, new TraceIdItem());
-  }
-
-  @Override
-  public AccessLogItem<RoutingContext> create(String rawPattern, AccessLogItemLocation location) {
-    return SIMPLE_ACCESSLOG_ITEM_MAP.get(location.getPlaceHolder());
-  }
-}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/impl/ResponseSizeItem.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/impl/ResponseSizeItem.java
index 0d58226fc..cb43bd5e2 100644
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/impl/ResponseSizeItem.java
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/impl/ResponseSizeItem.java
@@ -41,4 +41,8 @@ public String getFormattedItem(AccessLogParam<RoutingContext> accessLogParam) {
     long bytesWritten = response.bytesWritten();
     return 0 == bytesWritten ? zeroBytes : String.valueOf(bytesWritten);
   }
+
+  public String getZeroBytes() {
+    return zeroBytes;
+  }
 }
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/impl/AccessLogHandler.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/impl/AccessLogHandler.java
index 2acacb6f1..01b5676c5 100644
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/impl/AccessLogHandler.java
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/impl/AccessLogHandler.java
@@ -19,7 +19,6 @@
 
 import org.apache.servicecomb.transport.rest.vertx.accesslog.AccessLogGenerator;
 import org.apache.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogPatternParser;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -31,8 +30,8 @@
 
   private AccessLogGenerator accessLogGenerator;
 
-  public AccessLogHandler(String rawPattern, AccessLogPatternParser accessLogPatternParser) {
-    accessLogGenerator = new AccessLogGenerator(rawPattern, accessLogPatternParser);
+  public AccessLogHandler(String rawPattern) {
+    accessLogGenerator = new AccessLogGenerator(rawPattern);
   }
 
   @Override
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogItemCreator.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogItemCreator.java
new file mode 100644
index 000000000..ebd522b49
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogItemCreator.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.transport.rest.vertx.accesslog.parser;
+
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.AccessLogItem;
+
+/**
+ * The {@linkplain AccessLogItemCreator}s are able to instantiate a group of {@linkplain AccessLogItem}.
+ */
+public interface AccessLogItemCreator<T> {
+  /**
+   * Create an instance of {@linkplain AccessLogItem} which is specified by the config.
+   * @param config
+   * e.g. For {@linkplain org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.CookieItem CookieItem},
+   * the pattern may be "%{varName}C", and it's config is "varName". Some {@linkplain AccessLogItem} with no configurable
+   * pattern (like "%m") will receive {@code null} as config.
+   */
+  AccessLogItem<T> createItem(String config);
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogItemLocation.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogItemLocation.java
deleted file mode 100644
index 8389ea2fc..000000000
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogItemLocation.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.servicecomb.transport.rest.vertx.accesslog.parser;
-
-import org.apache.servicecomb.transport.rest.vertx.accesslog.placeholder.AccessLogItemTypeEnum;
-
-import com.google.common.base.Objects;
-
-public class AccessLogItemLocation {
-  private int start;
-
-  private int end;
-
-  private AccessLogItemTypeEnum placeHolder;
-
-  public int getStart() {
-    return start;
-  }
-
-  public AccessLogItemLocation setStart(int start) {
-    this.start = start;
-    return this;
-  }
-
-  public int getEnd() {
-    return end;
-  }
-
-  public AccessLogItemLocation setEnd(int end) {
-    this.end = end;
-    return this;
-  }
-
-  public AccessLogItemTypeEnum getPlaceHolder() {
-    return placeHolder;
-  }
-
-  public AccessLogItemLocation setPlaceHolder(AccessLogItemTypeEnum placeHolder) {
-    this.placeHolder = placeHolder;
-    return this;
-  }
-
-  @Override
-  public String toString() {
-    final StringBuilder sb = new StringBuilder("AccessLogItemLocation{");
-    sb.append("start=").append(start);
-    sb.append(", end=").append(end);
-    sb.append(", placeHolder=").append(placeHolder);
-    sb.append('}');
-    return sb.toString();
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) {
-      return true;
-    }
-    if (o == null || !getClass().isAssignableFrom(o.getClass())) {
-      return false;
-    }
-    AccessLogItemLocation that = (AccessLogItemLocation) o;
-    return start == that.start
-        && end == that.end
-        && placeHolder == that.placeHolder;
-  }
-
-  @Override
-  public int hashCode() {
-    return Objects.hashCode(start, end, placeHolder);
-  }
-}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogItemMeta.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogItemMeta.java
new file mode 100644
index 000000000..b47e5d1fc
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogItemMeta.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.transport.rest.vertx.accesslog.parser;
+
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.AccessLogItem;
+
+/**
+ * The meta data of {@linkplain AccessLogItem}.
+ */
+public class AccessLogItemMeta<T> {
+  protected String prefix;
+
+  protected String suffix;
+
+  /**
+   * Used for sorting {@linkplain AccessLogItemMeta}. Default value is 0.
+   * Smaller one has higher priority.
+   */
+  protected int order;
+
+  protected AccessLogItemCreator<T> accessLogItemCreator;
+
+  public String getPrefix() {
+    return prefix;
+  }
+
+  public AccessLogItemMeta<T> setPrefix(String prefix) {
+    this.prefix = prefix;
+    return this;
+  }
+
+  public String getSuffix() {
+    return suffix;
+  }
+
+  public AccessLogItemMeta<T> setSuffix(String suffix) {
+    this.suffix = suffix;
+    return this;
+  }
+
+  public int getOrder() {
+    return order;
+  }
+
+  public AccessLogItemMeta<T> setOrder(int order) {
+    this.order = order;
+    return this;
+  }
+
+  public AccessLogItemCreator<T> getAccessLogItemCreator() {
+    return accessLogItemCreator;
+  }
+
+  public AccessLogItemMeta<T> setAccessLogItemCreator(
+      AccessLogItemCreator<T> accessLogItemCreator) {
+    this.accessLogItemCreator = accessLogItemCreator;
+    return this;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogPatternParser.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogPatternParser.java
index 4e3f3fc9b..bc38768d4 100644
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogPatternParser.java
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogPatternParser.java
@@ -19,9 +19,15 @@
 
 import java.util.List;
 
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.AccessLogItem;
+
 /**
- * Parse the raw pattern, and generate a list of information about each access log item.
+ * This parser will parse the rawPattern of access log and generate a list of {@link AccessLogItem},
+ * which will be used in {@link org.apache.servicecomb.transport.rest.vertx.accesslog.AccessLogGenerator} to generate
+ * access log content.
+ * @param <T> the type of {@linkplain org.apache.servicecomb.transport.rest.vertx.accesslog.AccessLogParam#contextData
+ * AccessLogParam.contextData}, which usually depends on the transport way.
  */
-public interface AccessLogPatternParser {
-  List<AccessLogItemLocation> parsePattern(String rawPattern);
+public interface AccessLogPatternParser<T> {
+  List<AccessLogItem<T>> parsePattern(String rawPattern);
 }
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/matcher/AccessLogItemMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/CompositeVertxRestAccessLogItemMeta.java
similarity index 60%
rename from transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/matcher/AccessLogItemMatcher.java
rename to transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/CompositeVertxRestAccessLogItemMeta.java
index 67d8b6771..2ad687fcb 100644
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/matcher/AccessLogItemMatcher.java
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/CompositeVertxRestAccessLogItemMeta.java
@@ -15,14 +15,17 @@
  * limitations under the License.
  */
 
-package org.apache.servicecomb.transport.rest.vertx.accesslog.parser.matcher;
+package org.apache.servicecomb.transport.rest.vertx.accesslog.parser;
 
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemLocation;
+import java.util.List;
 
-public interface AccessLogItemMatcher {
-  /*
-   * Return an {@link AccessLogItemLocation} which matches part of rawPattern and is nearest to the offset(That means
-   * the {@link AccessLogItemLocation#start} is no less than offset and is smallest among the potential matched Item).
-   */
-  AccessLogItemLocation match(String rawPattern, int offset);
+/**
+ * Hold a group of {@link VertxRestAccessLogItemMeta} so that user can define
+ * only one VertxRestAccessLogItemMeta in spi loading file and load a group of meta.
+ * <p/>
+ * Once the access log loading mechanism finds that a meta is CompositeVertxRestAccessLogItemMeta,
+ * the meta hold by it will be used in access log while this meta itself will be ignored.
+ */
+public abstract class CompositeVertxRestAccessLogItemMeta extends VertxRestAccessLogItemMeta {
+  public abstract List<VertxRestAccessLogItemMeta> getAccessLogItemMetas();
 }
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/VertxRestAccessLogItemMeta.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/VertxRestAccessLogItemMeta.java
new file mode 100644
index 000000000..7f7916ff5
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/VertxRestAccessLogItemMeta.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.transport.rest.vertx.accesslog.parser;
+
+import io.vertx.ext.web.RoutingContext;
+
+/**
+ * For vertx-rest transport way.
+ */
+public class VertxRestAccessLogItemMeta extends AccessLogItemMeta<RoutingContext> {
+  public VertxRestAccessLogItemMeta() {
+  }
+
+  public VertxRestAccessLogItemMeta(String prefix, String suffix,
+      AccessLogItemCreator<RoutingContext> accessLogItemCreator, int order) {
+    this.prefix = prefix;
+    this.suffix = suffix;
+    this.accessLogItemCreator = accessLogItemCreator;
+    this.order = order;
+  }
+
+  public VertxRestAccessLogItemMeta(String prefix, AccessLogItemCreator<RoutingContext> accessLogItemCreator) {
+    this(prefix, null, accessLogItemCreator, 0);
+  }
+
+  public VertxRestAccessLogItemMeta(String prefix, String suffix,
+      AccessLogItemCreator<RoutingContext> accessLogItemCreator) {
+    this(prefix, suffix, accessLogItemCreator, 0);
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/DefaultAccessLogPatternParser.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/DefaultAccessLogPatternParser.java
deleted file mode 100644
index 05e56496a..000000000
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/DefaultAccessLogPatternParser.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.servicecomb.transport.rest.vertx.accesslog.parser.impl;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemLocation;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogPatternParser;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.matcher.AccessLogItemMatcher;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.matcher.PercentagePrefixConfigurableMatcher;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.matcher.SimpleItemMatcher;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.placeholder.AccessLogItemTypeEnum;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class DefaultAccessLogPatternParser implements AccessLogPatternParser {
-  private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAccessLogPatternParser.class);
-
-  private static final List<AccessLogItemMatcher> matcherList = Arrays.asList(
-      new SimpleItemMatcher(), new PercentagePrefixConfigurableMatcher()
-  );
-
-  /**
-   * locate all kinds of access log item, and mark their type.
-   */
-  @Override
-  public List<AccessLogItemLocation> parsePattern(String rawPattern) {
-    LOGGER.info("parse access log pattern: [{}]", rawPattern);
-    List<AccessLogItemLocation> locationList = new ArrayList<>();
-    for (int i = 0; i < rawPattern.length(); ) {
-      AccessLogItemLocation location = match(rawPattern, i);
-      if (null == location) {
-        break;
-      }
-
-      locationList.add(location);
-      i = location.getEnd();
-    }
-
-    checkLocationList(rawPattern, locationList);
-
-    locationList = fillInTextPlain(rawPattern, locationList);
-
-    return locationList;
-  }
-
-  /**
-   * find out a placeholder that occurs firstly behind the offset index.
-   */
-  private AccessLogItemLocation match(String rawPattern, int offset) {
-    AccessLogItemLocation result = null;
-    for (AccessLogItemMatcher matcher : matcherList) {
-      AccessLogItemLocation location = matcher.match(rawPattern, offset);
-      if ((null == result) || (null != location && location.getStart() < result.getStart())) {
-        // if result is null or location is nearer to offset, use location as result
-        result = location;
-      }
-    }
-    return result;
-  }
-
-  /**
-   * The content not matched in rawPattern will be printed as it is, so should be converted to {@link AccessLogItemTypeEnum#TEXT_PLAIN}
-   * @param rawPattern access log string pattern
-   * @param locationList {@link AccessLogItemLocation} list indicating the position of each access log item
-   */
-  private List<AccessLogItemLocation> fillInTextPlain(String rawPattern, List<AccessLogItemLocation> locationList) {
-    int cursor = 0;
-    List<AccessLogItemLocation> result = new ArrayList<>();
-
-    for (AccessLogItemLocation location : locationList) {
-      if (cursor == location.getStart()) {
-        result.add(location);
-      } else if (cursor < location.getStart()) {
-        result.add(new AccessLogItemLocation().setStart(cursor).setEnd(location.getStart()).setPlaceHolder(
-            AccessLogItemTypeEnum.TEXT_PLAIN));
-        result.add(location);
-      }
-      cursor = location.getEnd();
-    }
-
-    if (cursor < rawPattern.length()) {
-      result.add(new AccessLogItemLocation().setStart(cursor).setEnd(rawPattern.length())
-          .setPlaceHolder(AccessLogItemTypeEnum.TEXT_PLAIN));
-    }
-
-    return result;
-  }
-
-  /**
-   * If the access log items' location overlaps or is illegal(exceeding the boundary of the rawPattern),
-   * a {@link IllegalArgumentException} will be thrown out.
-   */
-  private void checkLocationList(String rawPattern, List<AccessLogItemLocation> locationList) {
-    int preEnd = -1;
-    for (AccessLogItemLocation location : locationList) {
-      if (preEnd > location.getStart()) {
-        throw new IllegalArgumentException("access log pattern contains illegal placeholder, please check it.");
-      }
-
-      preEnd = location.getEnd();
-    }
-
-    if (preEnd > rawPattern.length()) {
-      throw new IllegalArgumentException("access log pattern contains illegal placeholder, please check it.");
-    }
-  }
-}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/DefaultCompositeVertxRestAccessLogItemMeta.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/DefaultCompositeVertxRestAccessLogItemMeta.java
new file mode 100644
index 000000000..aaaf9f0f0
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/DefaultCompositeVertxRestAccessLogItemMeta.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.transport.rest.vertx.accesslog.parser.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.AccessLogItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.CookieItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.DatetimeConfigurableItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.DurationMillisecondItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.DurationSecondItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.FirstLineOfRequestItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.HttpMethodItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.HttpStatusItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.InvocationContextItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.LocalHostItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.LocalPortItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.QueryStringItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.RemoteHostItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.RequestHeaderItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.RequestProtocolItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.ResponseHeaderItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.ResponseSizeItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.TraceIdItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.UrlPathItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.UrlPathWithQueryItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.CompositeVertxRestAccessLogItemMeta;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.VertxRestAccessLogItemMeta;
+
+import io.vertx.ext.web.RoutingContext;
+
+public class DefaultCompositeVertxRestAccessLogItemMeta extends CompositeVertxRestAccessLogItemMeta {
+  private static final List<VertxRestAccessLogItemMeta> SUPPORTED_META = new ArrayList<>();
+
+  static {
+    final AccessLogItem<RoutingContext> httpMethodItem = new HttpMethodItem();
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%m", config -> httpMethodItem));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("cs-method", config -> httpMethodItem));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%s", config -> new HttpStatusItem()));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("sc-status", config -> new HttpStatusItem()));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%T", config -> new DurationSecondItem()));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%D", config -> new DurationMillisecondItem()));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%h", config -> new RemoteHostItem()));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%v", config -> new LocalHostItem()));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%p", config -> new LocalPortItem()));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%B", config -> new ResponseSizeItem("0")));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%b", config -> new ResponseSizeItem("-")));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%r", config -> new FirstLineOfRequestItem()));
+    final AccessLogItem<RoutingContext> urlPathItem = new UrlPathItem();
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%U", config -> urlPathItem));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("cs-uri-stem", config -> urlPathItem));
+    final AccessLogItem<RoutingContext> queryStringItem = new QueryStringItem();
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%q", config -> queryStringItem));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("cs-uri-query", config -> queryStringItem));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("cs-uri", config -> new UrlPathWithQueryItem()));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%H", config -> new RequestProtocolItem()));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%t", config -> new DatetimeConfigurableItem()));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%SCB-traceId", config -> new TraceIdItem()));
+
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%{", "}t", DatetimeConfigurableItem::new));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%{", "}i", RequestHeaderItem::new));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%{", "}o", ResponseHeaderItem::new));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%{", "}C", CookieItem::new));
+    SUPPORTED_META.add(new VertxRestAccessLogItemMeta("%{", "}SCB-ctx", InvocationContextItem::new));
+  }
+
+  @Override
+  public List<VertxRestAccessLogItemMeta> getAccessLogItemMetas() {
+    return SUPPORTED_META;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/VertxRestAccessLogPatternParser.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/VertxRestAccessLogPatternParser.java
new file mode 100644
index 000000000..7fdb2f928
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/VertxRestAccessLogPatternParser.java
@@ -0,0 +1,350 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.transport.rest.vertx.accesslog.parser.impl;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.AccessLogItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.PlainTextItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemMeta;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogPatternParser;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.CompositeVertxRestAccessLogItemMeta;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.VertxRestAccessLogItemMeta;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vertx.ext.web.RoutingContext;
+
+/**
+ * The parser is used for rest-over-vertx transport.
+ */
+public class VertxRestAccessLogPatternParser implements AccessLogPatternParser<RoutingContext> {
+  private static final Logger LOGGER = LoggerFactory.getLogger(VertxRestAccessLogPatternParser.class);
+
+  public static final Comparator<VertxRestAccessLogItemMeta> accessLogItemMetaComparator = (m1, m2) -> {
+    int result = m1.getOrder() - m2.getOrder();
+    if (result != 0) {
+      return result;
+    }
+
+    // one of m1 & m2 has suffix, but the other one doesn't have
+    if (m1.getSuffix() == null ^ m2.getSuffix() == null) {
+      return m1.getSuffix() == null ? 1 : -1;
+    }
+
+    if (null != m1.getSuffix()) {
+      result = comparePlaceholderString(m1.getSuffix(), m2.getSuffix());
+    }
+
+    return 0 == result ?
+        comparePlaceholderString(m1.getPrefix(), m2.getPrefix())
+        : result;
+  };
+
+  private List<VertxRestAccessLogItemMeta> metaList = new ArrayList<>();
+
+  public VertxRestAccessLogPatternParser() {
+    List<VertxRestAccessLogItemMeta> loadedMeta = loadVertxRestAccessLogItemMeta();
+    if (null == loadedMeta || loadedMeta.isEmpty()) {
+      LOGGER.error("cannot load AccessLogItemMeta!");
+      throw new IllegalStateException("cannot load AccessLogItemMeta!");
+    }
+    for (VertxRestAccessLogItemMeta meta : loadedMeta) {
+      if (CompositeVertxRestAccessLogItemMeta.class.isAssignableFrom(meta.getClass())) {
+        this.metaList.addAll(((CompositeVertxRestAccessLogItemMeta) meta).getAccessLogItemMetas());
+      } else {
+        this.metaList.add(meta);
+      }
+    }
+    sortAccessLogItemMeta(this.metaList);
+  }
+
+  private List<VertxRestAccessLogItemMeta> loadVertxRestAccessLogItemMeta() {
+    return SPIServiceUtils.getOrLoadSortedService(VertxRestAccessLogItemMeta.class);
+  }
+
+  /**
+   * Behavior of this compare:
+   * 1. comparePlaceholderString("abc","bbc") < 0
+   * 2. comparePlaceholderString("abc","ab") < 0
+   * 3. comparePlaceholderString("abc","abc") = 0
+   */
+  public static int comparePlaceholderString(String s1, String s2) {
+    int result = s1.compareTo(s2);
+    if (0 == result) {
+      return result;
+    }
+
+    // there are two possible cases:
+    // 1. s1="ab", s2="def"
+    // 2. s1="ab", s2="abc"
+    // in the case1 just return the result, but int the case2 the result should be reversed
+    return result < 0 ?
+        (s2.startsWith(s1) ? -result : result)
+        : (s1.startsWith(s2) ? -result : result);
+  }
+
+  /**
+   * Sort all of the {@link AccessLogItemMeta}, the meta that is in front of the others has higher priority.
+   * <p/>
+   * Sort rule(priority decreased):
+   * <ol>
+   *   <li>compare the {@link AccessLogItemMeta#order}</li>
+   *   <li>compare the {@link AccessLogItemMeta#suffix} in lexicographic order, if one's suffix is start with
+   *   the other one's suffix, this one(who's suffix is longer) has higher priority</li>
+   *   <li>compare the {@link AccessLogItemMeta#prefix}, compare rule is the same as suffix.</li>
+   * </ol>
+   * <p/>
+   * e.g. given a list of {@link AccessLogItemMeta} like below:
+   * <ol>
+   * <li>(%ac{,}bcd)</li>
+   * <li>(%ac{,}bc)</li>
+   * <li>(%ac{,}a)</li>
+   * <li>(%ac,)</li>
+   * <li>(%b,)</li>
+   * <li>(%a)</li>
+   * <li>(%{,}b)</li>
+   * <li>(%{,}bc)</li>
+   * </ol>
+   * the result is:
+   * <ol>
+   * <li>(%ac{,}a)</li>
+   * <li>(%ac{,}bcd)</li>
+   * <li>(%ac{,}bc)</li>
+   * <li>(%{,}bc)</li>
+   * <li>(%{,}b)</li>
+   * <li>(%ac,)</li>
+   * <li>(%a)</li>
+   * <li>(%b,)</li>
+   * </ol>
+   */
+  public static void sortAccessLogItemMeta(List<VertxRestAccessLogItemMeta> accessLogItemMetaList) {
+    accessLogItemMetaList.sort(accessLogItemMetaComparator);
+  }
+
+  /**
+   * @param rawPattern The access log pattern string specified by users.
+   * @return A list of {@linkplain AccessLogItem} which actually generate the content of access log.
+   */
+  @Override
+  public List<AccessLogItem<RoutingContext>> parsePattern(String rawPattern) {
+    LOGGER.info("parse the pattern of access log: [{}]", rawPattern);
+    List<AccessLogItemLocation> locationList = matchAccessLogItem(rawPattern);
+    locationList = fillInPlainTextLocation(rawPattern, locationList);
+
+    return convertToItemList(rawPattern, locationList);
+  }
+
+  /**
+   * Use the {@link #metaList} to match rawPattern.
+   * Return a list of {@link AccessLogItemLocation}.
+   * Plain text is ignored.
+   */
+  private List<AccessLogItemLocation> matchAccessLogItem(String rawPattern) {
+    List<AccessLogItemLocation> locationList = new ArrayList<>();
+    int cursor = 0;
+    while (cursor < rawPattern.length()) {
+      AccessLogItemLocation candidate = null;
+      for (VertxRestAccessLogItemMeta meta : metaList) {
+        if (null != candidate && null == meta.getSuffix()) {
+          // TODO:
+          // if user define item("%{","}ab") and item("%{_","}abc") and the pattern is "%{_var}ab}abc"
+          // currently the result is item("%{","_var","}ab"), plaintext("}abc")
+          // is this acceptable?
+
+          // We've gotten an AccessLogItem with suffix, so there is no need to match those without suffix,
+          // just break this match loop
+          cursor = candidate.tail;
+          break;
+        }
+        if (rawPattern.startsWith(meta.getPrefix(), cursor)) {
+          if (null == meta.getSuffix()) {
+            // for simple type AccessLogItem, there is no need to try to match the next item.
+            candidate = new AccessLogItemLocation(cursor, meta);
+            cursor = candidate.tail;
+            break;
+          }
+          // for configurable type, like %{...}i, more check is needed
+          // e.g. "%{varName1}o ${varName2}i" should be divided into
+          // ResponseHeaderItem with varName="varName1" and RequestHeaderItem with varName="varName2"
+          // INSTEAD OF RequestHeaderItem with varName="varName1}o ${varName2"
+          int rear = rawPattern.indexOf(meta.getSuffix(), cursor);
+          if (rear < 0) {
+            continue;
+          }
+          if (null == candidate || rear < candidate.suffixIndex) {
+            candidate = new AccessLogItemLocation(cursor, rear, meta);
+          }
+          // There is a matched item which is in front of this item, so this item is ignored.
+        }
+      }
+
+      if (candidate == null) {
+        ++cursor;
+        continue;
+      }
+      locationList.add(candidate);
+    }
+
+    return locationList;
+  }
+
+  /**
+   * After processing of {@link #matchAccessLogItem(String)}, all of the placeholders of {@link AccessLogItem} have been
+   * picked out. So the rest part of rawPattern should be treated as plain text. Those parts will be located in this
+   * method and wrapped as {@link PlainTextItem}.
+   * @param rawPattern raw pattern string of access log
+   * @param locationList locations picked out by {@link #matchAccessLogItem(String)}
+   * @return all of the locations including {@link PlainTextItem}.
+   */
+  private List<AccessLogItemLocation> fillInPlainTextLocation(String rawPattern,
+      List<AccessLogItemLocation> locationList) {
+    List<AccessLogItemLocation> resultList = new ArrayList<>();
+    if (locationList.isEmpty()) {
+      resultList.add(createTextPlainItemLocation(0, rawPattern.length()));
+      return resultList;
+    }
+
+    Iterator<AccessLogItemLocation> itemLocationIterator = locationList.iterator();
+    AccessLogItemLocation previousItemLocation = itemLocationIterator.next();
+    if (previousItemLocation.prefixIndex > 0) {
+      resultList.add(createTextPlainItemLocation(0, previousItemLocation.prefixIndex));
+    }
+    resultList.add(previousItemLocation);
+
+    while (itemLocationIterator.hasNext()) {
+      AccessLogItemLocation thisItemLocation = itemLocationIterator.next();
+      if (previousItemLocation.tail < thisItemLocation.prefixIndex) {
+        resultList.add(createTextPlainItemLocation(previousItemLocation.tail, thisItemLocation.prefixIndex));
+      }
+      previousItemLocation = thisItemLocation;
+      resultList.add(previousItemLocation);
+    }
+
+    if (previousItemLocation.tail < rawPattern.length()) {
+      resultList.add(createTextPlainItemLocation(
+          previousItemLocation.tail,
+          rawPattern.length()));
+    }
+    return resultList;
+  }
+
+  private AccessLogItemLocation createTextPlainItemLocation(int front, int rear) {
+    return new AccessLogItemLocation(front, rear);
+  }
+
+  private List<AccessLogItem<RoutingContext>> convertToItemList(String rawPattern,
+      List<AccessLogItemLocation> locationList) {
+    List<AccessLogItem<RoutingContext>> itemList = new ArrayList<>();
+
+    for (AccessLogItemLocation accessLogItemLocation : locationList) {
+      VertxRestAccessLogItemMeta accessLogItemMeta = accessLogItemLocation.accessLogItemMeta;
+      if (null == accessLogItemMeta) {
+        // a PlainTextItem location
+        itemList.add(new PlainTextItem(rawPattern.substring(
+            accessLogItemLocation.prefixIndex, accessLogItemLocation.tail
+        )));
+        continue;
+      }
+
+      itemList.add(
+          accessLogItemMeta.getAccessLogItemCreator().createItem(
+              getConfigString(rawPattern, accessLogItemLocation))
+      );
+    }
+
+    return itemList;
+  }
+
+  private String getConfigString(String rawPattern, AccessLogItemLocation accessLogItemLocation) {
+    if (null == accessLogItemLocation.getSuffix()) {
+      // simple AccessLogItem
+      return null;
+    }
+
+    return rawPattern.substring(
+        accessLogItemLocation.prefixIndex + accessLogItemLocation.getPrefix().length(),
+        accessLogItemLocation.suffixIndex);
+  }
+
+  private static class AccessLogItemLocation {
+    /**
+     * prefixIndex = rawPattern.indexOf(prefix)
+     */
+    int prefixIndex;
+
+    /**
+     * suffixIndex = rawPattern.indexOf(suffix)
+     */
+    int suffixIndex;
+
+    /**
+     * tail = suffixIndex + suffix.length()
+     */
+    int tail;
+
+    VertxRestAccessLogItemMeta accessLogItemMeta;
+
+    /**
+     * for {@link PlainTextItem} only
+     */
+    AccessLogItemLocation(int prefixIndex, int suffixIndex) {
+      this.prefixIndex = prefixIndex;
+      this.suffixIndex = suffixIndex;
+      this.tail = suffixIndex;
+    }
+
+    /**
+     * for configurable type AccessLogItem
+     */
+    AccessLogItemLocation(int prefixIndex, int suffixIndex, VertxRestAccessLogItemMeta accessLogItemMeta) {
+      this.prefixIndex = prefixIndex;
+      this.suffixIndex = suffixIndex;
+      this.tail = suffixIndex + accessLogItemMeta.getSuffix().length();
+      this.accessLogItemMeta = accessLogItemMeta;
+    }
+
+    /**
+     * for simple type AccessLogItem
+     */
+    AccessLogItemLocation(int prefixIndex, VertxRestAccessLogItemMeta accessLogItemMeta) {
+      this.prefixIndex = prefixIndex;
+      this.suffixIndex = prefixIndex + accessLogItemMeta.getPrefix().length();
+      this.tail = this.suffixIndex;
+      this.accessLogItemMeta = accessLogItemMeta;
+    }
+
+    public String getPrefix() {
+      if (null == accessLogItemMeta) {
+        return null;
+      }
+      return accessLogItemMeta.getPrefix();
+    }
+
+    public String getSuffix() {
+      if (null == accessLogItemMeta) {
+        return null;
+      }
+      return accessLogItemMeta.getSuffix();
+    }
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/matcher/PercentagePrefixConfigurableMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/matcher/PercentagePrefixConfigurableMatcher.java
deleted file mode 100644
index 55e9d6bf5..000000000
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/matcher/PercentagePrefixConfigurableMatcher.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.servicecomb.transport.rest.vertx.accesslog.parser.matcher;
-
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemLocation;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.placeholder.AccessLogItemTypeEnum;
-
-/**
- * for those access log item whose placeholder like "%{configpart}C"
- */
-public class PercentagePrefixConfigurableMatcher implements AccessLogItemMatcher {
-
-  public static final String GENERAL_PREFIX = "%{";
-
-  /**
-   * suffix to AccessLogItemTypeEnum
-   */
-  private static final Map<String, AccessLogItemTypeEnum> SUFFIX_PLACEHOLDER_ENUM_MAP = new LinkedHashMap<>();
-
-  /**
-   * AccessLogItemTypeEnum to suffix
-   */
-  private static final Map<AccessLogItemTypeEnum, String> ENUM_SUFFIX_MAP = new HashMap<>();
-
-  public static final String SUFFIX_HEAD = "}";
-
-  static {
-    SUFFIX_PLACEHOLDER_ENUM_MAP.put("}t", AccessLogItemTypeEnum.DATETIME_CONFIGURABLE);
-    SUFFIX_PLACEHOLDER_ENUM_MAP.put("}i", AccessLogItemTypeEnum.REQUEST_HEADER);
-    SUFFIX_PLACEHOLDER_ENUM_MAP.put("}o", AccessLogItemTypeEnum.RESPONSE_HEADER);
-    SUFFIX_PLACEHOLDER_ENUM_MAP.put("}C", AccessLogItemTypeEnum.COOKIE);
-    SUFFIX_PLACEHOLDER_ENUM_MAP.put("}SCB-ctx", AccessLogItemTypeEnum.SCB_INVOCATION_CONTEXT);
-
-    for (Entry<String, AccessLogItemTypeEnum> entry : SUFFIX_PLACEHOLDER_ENUM_MAP.entrySet()) {
-      ENUM_SUFFIX_MAP.put(entry.getValue(), entry.getKey());
-    }
-  }
-
-  @Override
-  public AccessLogItemLocation match(String rawPattern, int offset) {
-    int begin = rawPattern.indexOf(GENERAL_PREFIX, offset);
-    if (begin < 0) {
-      return null;
-    }
-
-    int end = rawPattern.indexOf(SUFFIX_HEAD, begin);
-    if (end < 0) {
-      return null;
-    }
-
-    for (Entry<String, AccessLogItemTypeEnum> entry : SUFFIX_PLACEHOLDER_ENUM_MAP.entrySet()) {
-      if (rawPattern.startsWith(entry.getKey(), end)) {
-        return new AccessLogItemLocation().setStart(begin).setEnd(end + entry.getKey().length())
-            .setPlaceHolder(entry.getValue());
-      }
-    }
-
-    return null;
-  }
-
-  public static String getSuffix(AccessLogItemTypeEnum accessLogItemTypeEnum) {
-    return ENUM_SUFFIX_MAP.get(accessLogItemTypeEnum);
-  }
-}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/matcher/SimpleItemMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/matcher/SimpleItemMatcher.java
deleted file mode 100644
index a3d6e9325..000000000
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/matcher/SimpleItemMatcher.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.servicecomb.transport.rest.vertx.accesslog.parser.matcher;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemLocation;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.placeholder.AccessLogItemTypeEnum;
-
-/**
- * for those access log items whose placeholder has no changeable part.
- */
-public class SimpleItemMatcher implements AccessLogItemMatcher {
-  private static final Map<String, AccessLogItemTypeEnum> PLACEHOLDER_ENUM_MAP = new LinkedHashMap<>();
-
-  static {
-    PLACEHOLDER_ENUM_MAP.put("%m", AccessLogItemTypeEnum.HTTP_METHOD);
-    PLACEHOLDER_ENUM_MAP.put("cs-method", AccessLogItemTypeEnum.HTTP_METHOD);
-    PLACEHOLDER_ENUM_MAP.put("%s", AccessLogItemTypeEnum.HTTP_STATUS);
-    PLACEHOLDER_ENUM_MAP.put("sc-status", AccessLogItemTypeEnum.HTTP_STATUS);
-    PLACEHOLDER_ENUM_MAP.put("%T", AccessLogItemTypeEnum.DURATION_IN_SECOND);
-    PLACEHOLDER_ENUM_MAP.put("%D", AccessLogItemTypeEnum.DURATION_IN_MILLISECOND);
-    PLACEHOLDER_ENUM_MAP.put("%h", AccessLogItemTypeEnum.REMOTE_HOSTNAME);
-    PLACEHOLDER_ENUM_MAP.put("%v", AccessLogItemTypeEnum.LOCAL_HOSTNAME);
-    PLACEHOLDER_ENUM_MAP.put("%p", AccessLogItemTypeEnum.LOCAL_PORT);
-    PLACEHOLDER_ENUM_MAP.put("%B", AccessLogItemTypeEnum.RESPONSE_SIZE);
-    PLACEHOLDER_ENUM_MAP.put("%b", AccessLogItemTypeEnum.RESPONSE_SIZE_CLF);
-    PLACEHOLDER_ENUM_MAP.put("%r", AccessLogItemTypeEnum.FIRST_LINE_OF_REQUEST);
-    PLACEHOLDER_ENUM_MAP.put("%U", AccessLogItemTypeEnum.URL_PATH);
-    PLACEHOLDER_ENUM_MAP.put("cs-uri-stem", AccessLogItemTypeEnum.URL_PATH);
-    PLACEHOLDER_ENUM_MAP.put("%q", AccessLogItemTypeEnum.QUERY_STRING);
-    PLACEHOLDER_ENUM_MAP.put("cs-uri-query", AccessLogItemTypeEnum.QUERY_STRING);
-    PLACEHOLDER_ENUM_MAP.put("cs-uri", AccessLogItemTypeEnum.URL_PATH_WITH_QUERY);
-    PLACEHOLDER_ENUM_MAP.put("%H", AccessLogItemTypeEnum.REQUEST_PROTOCOL);
-    PLACEHOLDER_ENUM_MAP.put("%t", AccessLogItemTypeEnum.DATETIME_DEFAULT);
-    PLACEHOLDER_ENUM_MAP.put("%SCB-traceId", AccessLogItemTypeEnum.SCB_TRACE_ID);
-  }
-
-  @Override
-  public AccessLogItemLocation match(String rawPattern, int offset) {
-    int start = -1;
-    Entry<String, AccessLogItemTypeEnum> nearestEntry = null;
-    for (Entry<String, AccessLogItemTypeEnum> entry : PLACEHOLDER_ENUM_MAP.entrySet()) {
-      int cursor = rawPattern.indexOf(entry.getKey(), offset);
-      if (cursor < 0) {
-        continue;
-      }
-      if (start < 0 || cursor < start) {
-        start = cursor;
-        nearestEntry = entry;
-      }
-    }
-
-    if (null == nearestEntry) {
-      return null;
-    }
-
-    return new AccessLogItemLocation().setStart(start).setEnd(start + nearestEntry.getKey().length())
-        .setPlaceHolder(nearestEntry.getValue());
-  }
-}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.accesslog.parser.VertxRestAccessLogItemMeta b/transports/transport-rest/transport-rest-vertx/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.accesslog.parser.VertxRestAccessLogItemMeta
new file mode 100644
index 000000000..7a9f86cc4
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.accesslog.parser.VertxRestAccessLogItemMeta
@@ -0,0 +1,18 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.apache.servicecomb.transport.rest.vertx.accesslog.parser.impl.DefaultCompositeVertxRestAccessLogItemMeta
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/AccessLogGeneratorTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/AccessLogGeneratorTest.java
index a8a1186b1..1df6609d4 100644
--- a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/AccessLogGeneratorTest.java
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/AccessLogGeneratorTest.java
@@ -20,15 +20,12 @@
 import static org.junit.Assert.assertEquals;
 
 import java.text.SimpleDateFormat;
-import java.util.Arrays;
 import java.util.TimeZone;
 
 import org.apache.servicecomb.transport.rest.vertx.accesslog.element.AccessLogItem;
 import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.DatetimeConfigurableItem;
 import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.HttpMethodItem;
 import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.PlainTextItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemLocation;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.placeholder.AccessLogItemTypeEnum;
 import org.junit.Assert;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -40,16 +37,7 @@
 
 public class AccessLogGeneratorTest {
 
-  private static final AccessLogGenerator ACCESS_LOG_GENERATOR = new AccessLogGenerator("%m - %t",
-      rawPattern -> {
-        assertEquals("%m - %t", rawPattern);
-        return Arrays.asList(
-            new AccessLogItemLocation().setStart(0).setEnd(2).setPlaceHolder(AccessLogItemTypeEnum.HTTP_METHOD),
-            new AccessLogItemLocation().setStart(2).setEnd(5).setPlaceHolder(AccessLogItemTypeEnum.TEXT_PLAIN),
-            new AccessLogItemLocation().setStart(5)
-                .setEnd(7)
-                .setPlaceHolder(AccessLogItemTypeEnum.DATETIME_DEFAULT));
-      });
+  private static final AccessLogGenerator ACCESS_LOG_GENERATOR = new AccessLogGenerator("%m - %t");
 
   @Test
   public void testConstructor() {
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/AccessLogItemFactoryTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/AccessLogItemFactoryTest.java
deleted file mode 100644
index 94b16e225..000000000
--- a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/AccessLogItemFactoryTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.servicecomb.transport.rest.vertx.accesslog.element;
-
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.DatetimeConfigurableItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.PlainTextItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.QueryStringItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.RequestHeaderItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.UrlPathWithQueryItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemLocation;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.placeholder.AccessLogItemTypeEnum;
-import org.junit.Assert;
-import org.junit.Test;
-
-import io.vertx.ext.web.RoutingContext;
-
-public class AccessLogItemFactoryTest {
-  private static final String PATTERN = "test %{EEE, dd MMM yyyy HH:mm:ss zzz}t cs-uri-query cs-uri %{VARNAME1}i";
-
-  private static final List<AccessLogItemLocation> locationList = Arrays.asList(
-      new AccessLogItemLocation().setStart(0).setEnd(5).setPlaceHolder(AccessLogItemTypeEnum.TEXT_PLAIN),
-      new AccessLogItemLocation().setStart(5).setEnd(38).setPlaceHolder(AccessLogItemTypeEnum.DATETIME_CONFIGURABLE),
-      new AccessLogItemLocation().setStart(39).setEnd(51).setPlaceHolder(AccessLogItemTypeEnum.QUERY_STRING),
-      new AccessLogItemLocation().setStart(52).setEnd(58).setPlaceHolder(AccessLogItemTypeEnum.URL_PATH_WITH_QUERY),
-      new AccessLogItemLocation().setStart(59).setEnd(71).setPlaceHolder(AccessLogItemTypeEnum.REQUEST_HEADER));
-
-  @Test
-  public void testCreateAccessLogItem() {
-    List<AccessLogItem<RoutingContext>> itemList =
-        new AccessLogItemFactory().createAccessLogItem(PATTERN, locationList);
-    Assert.assertEquals(5, itemList.size());
-    Assert.assertEquals(PlainTextItem.class, itemList.get(0).getClass());
-    Assert.assertEquals(DatetimeConfigurableItem.class, itemList.get(1).getClass());
-    Assert.assertEquals(QueryStringItem.class, itemList.get(2).getClass());
-    Assert.assertEquals(UrlPathWithQueryItem.class, itemList.get(3).getClass());
-    Assert.assertEquals(RequestHeaderItem.class, itemList.get(4).getClass());
-  }
-}
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/creator/PercentagePrefixConfigurableItemCreatorTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/creator/PercentagePrefixConfigurableItemCreatorTest.java
deleted file mode 100644
index cf3c343e7..000000000
--- a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/creator/PercentagePrefixConfigurableItemCreatorTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.servicecomb.transport.rest.vertx.accesslog.element.creator;
-
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.AccessLogItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.CookieItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.DatetimeConfigurableItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.InvocationContextItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.PlainTextItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.RequestHeaderItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.ResponseHeaderItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemLocation;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.placeholder.AccessLogItemTypeEnum;
-import org.junit.Assert;
-import org.junit.Test;
-
-import io.vertx.ext.web.RoutingContext;
-
-public class PercentagePrefixConfigurableItemCreatorTest {
-  private static final String PATTERN = "test %{EEE, dd MMM yyyy HH:mm:ss zzz}t %{VARNAME1}i %{VARNAME2}o %{VARNAME3}C %{var name4}SCB-ctx";
-
-  private static final PercentagePrefixConfigurableItemCreator CREATOR = new PercentagePrefixConfigurableItemCreator();
-
-  @Test
-  public void testCreateDatetimeConfigurableItem() {
-    AccessLogItemLocation location = new AccessLogItemLocation().setStart(5).setEnd(38).setPlaceHolder(
-        AccessLogItemTypeEnum.DATETIME_CONFIGURABLE);
-
-    AccessLogItem<RoutingContext> item = CREATOR.create(PATTERN, location);
-
-    Assert.assertEquals(DatetimeConfigurableItem.class, item.getClass());
-    Assert.assertEquals("EEE, dd MMM yyyy HH:mm:ss zzz", ((DatetimeConfigurableItem) item).getPattern());
-  }
-
-  @Test
-  public void testCreateRequestHeaderItem() {
-    AccessLogItemLocation location = new AccessLogItemLocation().setStart(39).setEnd(51).setPlaceHolder(
-        AccessLogItemTypeEnum.REQUEST_HEADER);
-
-    AccessLogItem<RoutingContext> item = CREATOR.create(PATTERN, location);
-
-    Assert.assertEquals(RequestHeaderItem.class, item.getClass());
-    Assert.assertEquals("VARNAME1", ((RequestHeaderItem) item).getVarName());
-  }
-
-  @Test
-  public void testCreateResponseHeaderItem() {
-    AccessLogItemLocation location = new AccessLogItemLocation().setStart(52).setEnd(64).setPlaceHolder(
-        AccessLogItemTypeEnum.RESPONSE_HEADER);
-
-    AccessLogItem<RoutingContext> item = CREATOR.create(PATTERN, location);
-
-    Assert.assertEquals(ResponseHeaderItem.class, item.getClass());
-    Assert.assertEquals("VARNAME2", ((ResponseHeaderItem) item).getVarName());
-  }
-
-  @Test
-  public void testCreateCookieItem() {
-    AccessLogItemLocation location = new AccessLogItemLocation().setStart(65).setEnd(77).setPlaceHolder(
-        AccessLogItemTypeEnum.COOKIE);
-
-    AccessLogItem<RoutingContext> item = CREATOR.create(PATTERN, location);
-
-    Assert.assertEquals(CookieItem.class, item.getClass());
-    Assert.assertEquals("VARNAME3", ((CookieItem) item).getVarName());
-  }
-
-  @Test
-  public void testPlainTextItem() {
-    AccessLogItemLocation location = new AccessLogItemLocation().setStart(0)
-        .setEnd(5)
-        .setPlaceHolder(AccessLogItemTypeEnum.TEXT_PLAIN);
-
-    AccessLogItem<RoutingContext> item = CREATOR.create(PATTERN, location);
-
-    Assert.assertEquals(PlainTextItem.class, item.getClass());
-    Assert.assertEquals("test ", item.getFormattedItem(null));
-  }
-
-  @Test
-  public void testCreateInvocationContextItem() {
-    AccessLogItemLocation location = new AccessLogItemLocation().setStart(78)
-        .setEnd(97)
-        .setPlaceHolder(AccessLogItemTypeEnum.SCB_INVOCATION_CONTEXT);
-
-    AccessLogItem<RoutingContext> item = CREATOR.create(PATTERN, location);
-
-    Assert.assertEquals(InvocationContextItem.class, item.getClass());
-    Assert.assertEquals("var name4", ((InvocationContextItem) item).getVarName());
-  }
-}
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/impl/UserDefinedAccessLogItem.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/impl/UserDefinedAccessLogItem.java
new file mode 100644
index 000000000..a2559c673
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/impl/UserDefinedAccessLogItem.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import org.apache.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.AccessLogItem;
+
+import io.vertx.ext.web.RoutingContext;
+
+/**
+ * For access log extension test
+ */
+public class UserDefinedAccessLogItem implements AccessLogItem<RoutingContext> {
+  private String config;
+
+  public UserDefinedAccessLogItem(String config) {
+    this.config = config;
+  }
+
+  @Override
+  public String getFormattedItem(AccessLogParam<RoutingContext> accessLogParam) {
+    return "user-defined-" + config;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/impl/UserDefinedAccessLogItemLowPriority.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/impl/UserDefinedAccessLogItemLowPriority.java
new file mode 100644
index 000000000..eec7b29fc
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/impl/UserDefinedAccessLogItemLowPriority.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import org.apache.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.AccessLogItem;
+
+import io.vertx.ext.web.RoutingContext;
+
+/**
+ * For access log extension test, will be overridden by {@link RemoteHostItem}("%h"),
+ * and takes no effect.
+ */
+public class UserDefinedAccessLogItemLowPriority implements AccessLogItem<RoutingContext> {
+
+  @Override
+  public String getFormattedItem(AccessLogParam<RoutingContext> accessLogParam) {
+    return "OverriddenItem";
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/impl/AccessLogHandlerTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/impl/AccessLogHandlerTest.java
new file mode 100644
index 000000000..ce96d4956
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/impl/AccessLogHandlerTest.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.transport.rest.vertx.accesslog.impl;
+
+import java.util.HashSet;
+
+import javax.xml.ws.Holder;
+
+import org.apache.log4j.Level;
+import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.core.Handler;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.core.net.SocketAddress;
+import io.vertx.ext.web.Cookie;
+import io.vertx.ext.web.RoutingContext;
+import mockit.Mock;
+import mockit.MockUp;
+
+public class AccessLogHandlerTest {
+  private static final AccessLogHandler ACCESS_LOG_HANDLER = new AccessLogHandler(
+      "%h - - %s durationMillisecond=[%D] %{test-config}user-defined %{cookie-name}C");
+
+  private LogCollector logCollector;
+
+  @Before
+  public void setUp() {
+    logCollector = new LogCollector();
+    logCollector.setLogLevel("accesslog", Level.INFO);
+  }
+
+  @After
+  public void tearDown() {
+    logCollector.teardown();
+  }
+
+  @Test
+  public void testHandle() {
+    RoutingContext routingContext = Mockito.mock(RoutingContext.class);
+    HashSet<Cookie> cookies = new HashSet<>();
+    Cookie cookie = Mockito.mock(Cookie.class);
+    HttpServerResponse httpServerResponse = new MockUp<HttpServerResponse>() {
+      @Mock
+      public HttpServerResponse endHandler(Handler<Void> handler) {
+        handler.handle(null);
+        return null;
+      }
+
+      @Mock
+      public int getStatusCode() {
+        return 200;
+      }
+    }.getMockInstance();
+    HttpServerRequest httpServerRequest = Mockito.mock(HttpServerRequest.class);
+    SocketAddress socketAddress = Mockito.mock(SocketAddress.class);
+
+    Holder<Integer> counter = new Holder<>();
+    counter.value = 0;
+    new MockUp<System>() {
+      @Mock
+      long currentTimeMillis() {
+        if (counter.value < 1) {
+          ++counter.value;
+          return 1L;
+        }
+        return 123L;
+      }
+    };
+    cookies.add(cookie);
+    Mockito.when(cookie.getName()).thenReturn("cookie-name");
+    Mockito.when(cookie.getValue()).thenReturn("cookie-value");
+    Mockito.when(routingContext.cookies()).thenReturn(cookies);
+    Mockito.when(routingContext.response()).thenReturn(httpServerResponse);
+    Mockito.when(routingContext.request()).thenReturn(httpServerRequest);
+    Mockito.when(httpServerRequest.remoteAddress()).thenReturn(socketAddress);
+    Mockito.when(socketAddress.host()).thenReturn("localhost");
+
+    ACCESS_LOG_HANDLER.handle(routingContext);
+
+    Assert.assertEquals("localhost - - 200 durationMillisecond=[122] user-defined-test-config cookie-value",
+        logCollector.getEvents().get(0).getMessage());
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/DefaultAccessLogPatternParserTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/DefaultAccessLogPatternParserTest.java
deleted file mode 100644
index efd045436..000000000
--- a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/DefaultAccessLogPatternParserTest.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.servicecomb.transport.rest.vertx.accesslog.parser.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemLocation;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.placeholder.AccessLogItemTypeEnum;
-import org.hamcrest.Matchers;
-import org.junit.Test;
-
-import mockit.Deencapsulation;
-
-public class DefaultAccessLogPatternParserTest {
-  private static final String ROW_PATTERN = "[cs-method] %m %s%T%D%h%v%p%B%b%r%U%q"
-      + "cs-uri-stemcs-uri-querycs-uri%H%t%{yyyy MM dd HH:mm:ss zzz}t"
-      + "%{yyyy MM dd HH:mm:ss|GMT+0|en-US}t"
-      + "%{incoming-header}i"
-      + "%{outgoing-header}o"
-      + "%{cookie}C"
-      + "%SCB-traceId"
-      + "%{invocationContext}SCB-ctx";
-
-  private static DefaultAccessLogPatternParser accessLogPatternParser = new DefaultAccessLogPatternParser();
-
-  @Test
-  public void testParsePattern() {
-    List<AccessLogItemLocation> result = accessLogPatternParser.parsePattern(ROW_PATTERN);
-    assertEquals(28, result.size());
-
-    assertThat(result.stream().map(AccessLogItemLocation::getPlaceHolder)
-            .filter(Objects::nonNull).collect(Collectors.toList()),
-        Matchers.contains(
-            AccessLogItemTypeEnum.TEXT_PLAIN,
-            AccessLogItemTypeEnum.HTTP_METHOD,
-            AccessLogItemTypeEnum.TEXT_PLAIN,
-            AccessLogItemTypeEnum.HTTP_METHOD,
-            AccessLogItemTypeEnum.TEXT_PLAIN,
-            AccessLogItemTypeEnum.HTTP_STATUS,
-            AccessLogItemTypeEnum.DURATION_IN_SECOND,
-            AccessLogItemTypeEnum.DURATION_IN_MILLISECOND,
-            AccessLogItemTypeEnum.REMOTE_HOSTNAME,
-            AccessLogItemTypeEnum.LOCAL_HOSTNAME,
-            AccessLogItemTypeEnum.LOCAL_PORT,
-            AccessLogItemTypeEnum.RESPONSE_SIZE,
-            AccessLogItemTypeEnum.RESPONSE_SIZE_CLF,
-            AccessLogItemTypeEnum.FIRST_LINE_OF_REQUEST,
-            AccessLogItemTypeEnum.URL_PATH,
-            AccessLogItemTypeEnum.QUERY_STRING,
-            AccessLogItemTypeEnum.URL_PATH,
-            AccessLogItemTypeEnum.QUERY_STRING,
-            AccessLogItemTypeEnum.URL_PATH_WITH_QUERY,
-            AccessLogItemTypeEnum.REQUEST_PROTOCOL,
-            AccessLogItemTypeEnum.DATETIME_DEFAULT,
-            AccessLogItemTypeEnum.DATETIME_CONFIGURABLE,
-            AccessLogItemTypeEnum.DATETIME_CONFIGURABLE,
-            AccessLogItemTypeEnum.REQUEST_HEADER,
-            AccessLogItemTypeEnum.RESPONSE_HEADER,
-            AccessLogItemTypeEnum.COOKIE,
-            AccessLogItemTypeEnum.SCB_TRACE_ID,
-            AccessLogItemTypeEnum.SCB_INVOCATION_CONTEXT));
-  }
-
-  @Test
-  public void testCheckLocationList() {
-    List<AccessLogItemLocation> locationList = new ArrayList<>(3);
-    locationList.add(new AccessLogItemLocation().setStart(0).setEnd(3));
-    locationList.add(new AccessLogItemLocation().setStart(3).setEnd(6));
-    locationList.add(new AccessLogItemLocation().setStart(5).setEnd(9));
-
-    try {
-      Deencapsulation.invoke(new DefaultAccessLogPatternParser(), "checkLocationList",
-          "0123456789", locationList);
-      fail("expect an exception");
-    } catch (Exception e) {
-      assertEquals(IllegalArgumentException.class, e.getClass());
-      assertEquals("access log pattern contains illegal placeholder, please check it.", e.getMessage());
-    }
-  }
-
-  @Test
-  public void testCheckLocationListOnLocationEndGreaterThanPatternLength() {
-    List<AccessLogItemLocation> locationList = new ArrayList<>(3);
-    locationList.add(new AccessLogItemLocation().setStart(0).setEnd(3));
-    locationList.add(new AccessLogItemLocation().setStart(3).setEnd(6));
-    locationList.add(new AccessLogItemLocation().setStart(7).setEnd(9));
-
-    try {
-      Deencapsulation.invoke(new DefaultAccessLogPatternParser(), "checkLocationList",
-          "0123456", locationList);
-      fail("expect an exception");
-    } catch (Exception e) {
-      assertEquals(IllegalArgumentException.class, e.getClass());
-      assertEquals("access log pattern contains illegal placeholder, please check it.", e.getMessage());
-    }
-  }
-}
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/TestCompositeExtendedAccessLogItemMeta.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/TestCompositeExtendedAccessLogItemMeta.java
new file mode 100644
index 000000000..c226babbf
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/TestCompositeExtendedAccessLogItemMeta.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.transport.rest.vertx.accesslog.parser.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.UserDefinedAccessLogItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.CompositeVertxRestAccessLogItemMeta;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.VertxRestAccessLogItemMeta;
+
+public class TestCompositeExtendedAccessLogItemMeta extends CompositeVertxRestAccessLogItemMeta {
+  private static final List<VertxRestAccessLogItemMeta> META_LIST = new ArrayList<>();
+
+  static {
+    META_LIST.add(new VertxRestAccessLogItemMeta("%{", "}user-defined", UserDefinedAccessLogItem::new));
+  }
+
+  @Override
+  public List<VertxRestAccessLogItemMeta> getAccessLogItemMetas() {
+    return META_LIST;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/creator/AccessLogItemCreator.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/TestSingleExtendedAccessLogItemMeta.java
similarity index 71%
rename from transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/creator/AccessLogItemCreator.java
rename to transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/TestSingleExtendedAccessLogItemMeta.java
index 5a31f1fa2..9a2a1c730 100644
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/accesslog/element/creator/AccessLogItemCreator.java
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/TestSingleExtendedAccessLogItemMeta.java
@@ -15,14 +15,13 @@
  * limitations under the License.
  */
 
-package org.apache.servicecomb.transport.rest.vertx.accesslog.element.creator;
+package org.apache.servicecomb.transport.rest.vertx.accesslog.parser.impl;
 
-import org.apache.servicecomb.transport.rest.vertx.accesslog.element.AccessLogItem;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemLocation;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.UserDefinedAccessLogItemLowPriority;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.VertxRestAccessLogItemMeta;
 
-/**
- * The actual creator of AccessLogItem.
- */
-public interface AccessLogItemCreator<T> {
-  AccessLogItem<T> create(String rawPattern, AccessLogItemLocation location);
+public class TestSingleExtendedAccessLogItemMeta extends VertxRestAccessLogItemMeta {
+  public TestSingleExtendedAccessLogItemMeta() {
+    super("%h", null, config -> new UserDefinedAccessLogItemLowPriority(), 1);
+  }
 }
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/VertxRestAccessLogPatternParserTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/VertxRestAccessLogPatternParserTest.java
new file mode 100644
index 000000000..cf31d1583
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/impl/VertxRestAccessLogPatternParserTest.java
@@ -0,0 +1,322 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.transport.rest.vertx.accesslog.parser.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.AccessLogItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.CookieItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.DatetimeConfigurableItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.DurationMillisecondItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.DurationSecondItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.FirstLineOfRequestItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.HttpMethodItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.HttpStatusItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.InvocationContextItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.LocalHostItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.LocalPortItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.QueryStringItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.RemoteHostItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.RequestHeaderItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.RequestProtocolItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.ResponseHeaderItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.ResponseSizeItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.TraceIdItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.UrlPathItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.element.impl.UrlPathWithQueryItem;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.CompositeVertxRestAccessLogItemMeta;
+import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.VertxRestAccessLogItemMeta;
+import org.junit.Assert;
+import org.junit.Test;
+
+import io.vertx.ext.web.RoutingContext;
+import mockit.Deencapsulation;
+import mockit.Mock;
+import mockit.MockUp;
+
+public class VertxRestAccessLogPatternParserTest {
+  private static final String ROW_PATTERN = "[cs-method] %m %s%T%D%h%v%p%B%b%r%U%q"
+      + "cs-uri-stemcs-uri-querycs-uri%H%t%{yyyy MM dd HH:mm:ss zzz}t"
+      + "%{yyyy MM dd HH:mm:ss|GMT+0|en-US}t"
+      + "%{incoming-header}i"
+      + "%{outgoing-header}o"
+      + "%{cookie}C"
+      + "%SCB-traceId"
+      + "%{ctx}SCB-ctx";
+
+  private static VertxRestAccessLogPatternParser accessLogPatternParser = new VertxRestAccessLogPatternParser();
+
+  @Test
+  public void testParsePatternFullTest() {
+    List<AccessLogItem<RoutingContext>> result = accessLogPatternParser.parsePattern(ROW_PATTERN);
+    assertEquals(28, result.size());
+
+    assertEquals("[", result.get(0).getFormattedItem(null));
+    assertEquals(HttpMethodItem.class, result.get(1).getClass());
+    assertEquals("] ", result.get(2).getFormattedItem(null));
+    assertEquals(HttpMethodItem.class, result.get(3).getClass());
+    assertEquals(" ", result.get(4).getFormattedItem(null));
+    assertEquals(HttpStatusItem.class, result.get(5).getClass());
+    assertEquals(DurationSecondItem.class, result.get(6).getClass());
+    assertEquals(DurationMillisecondItem.class, result.get(7).getClass());
+    assertEquals(RemoteHostItem.class, result.get(8).getClass());
+    assertEquals(LocalHostItem.class, result.get(9).getClass());
+    assertEquals(LocalPortItem.class, result.get(10).getClass());
+    assertEquals(ResponseSizeItem.class, result.get(11).getClass());
+    assertEquals("0", ((ResponseSizeItem) result.get(11)).getZeroBytes());
+    assertEquals(ResponseSizeItem.class, result.get(12).getClass());
+    assertEquals("-", ((ResponseSizeItem) result.get(12)).getZeroBytes());
+    assertEquals(FirstLineOfRequestItem.class, result.get(13).getClass());
+    assertEquals(UrlPathItem.class, result.get(14).getClass());
+    assertEquals(QueryStringItem.class, result.get(15).getClass());
+    assertEquals(UrlPathItem.class, result.get(16).getClass());
+    assertEquals(QueryStringItem.class, result.get(17).getClass());
+    assertEquals(UrlPathWithQueryItem.class, result.get(18).getClass());
+    assertEquals(RequestProtocolItem.class, result.get(19).getClass());
+    assertEquals(DatetimeConfigurableItem.class, result.get(20).getClass());
+    assertEquals(DatetimeConfigurableItem.DEFAULT_DATETIME_PATTERN,
+        ((DatetimeConfigurableItem) result.get(20)).getPattern());
+    assertEquals(DatetimeConfigurableItem.DEFAULT_LOCALE, ((DatetimeConfigurableItem) result.get(20)).getLocale());
+    assertEquals(TimeZone.getDefault(), ((DatetimeConfigurableItem) result.get(20)).getTimezone());
+    assertEquals("yyyy MM dd HH:mm:ss zzz", ((DatetimeConfigurableItem) result.get(21)).getPattern());
+    assertEquals(DatetimeConfigurableItem.DEFAULT_LOCALE, ((DatetimeConfigurableItem) result.get(21)).getLocale());
+    assertEquals(TimeZone.getDefault(), ((DatetimeConfigurableItem) result.get(21)).getTimezone());
+    assertEquals("yyyy MM dd HH:mm:ss", ((DatetimeConfigurableItem) result.get(22)).getPattern());
+    assertEquals(Locale.forLanguageTag("en-US"), ((DatetimeConfigurableItem) result.get(22)).getLocale());
+    assertEquals(TimeZone.getTimeZone("GMT+0"), ((DatetimeConfigurableItem) result.get(22)).getTimezone());
+    assertEquals(RequestHeaderItem.class, result.get(23).getClass());
+    assertEquals("incoming-header", ((RequestHeaderItem) result.get(23)).getVarName());
+    assertEquals(ResponseHeaderItem.class, result.get(24).getClass());
+    assertEquals("outgoing-header", ((ResponseHeaderItem) result.get(24)).getVarName());
+    assertEquals(CookieItem.class, result.get(25).getClass());
+    assertEquals("cookie", ((CookieItem) result.get(25)).getVarName());
+    assertEquals(TraceIdItem.class, result.get(26).getClass());
+    assertEquals(InvocationContextItem.class, result.get(27).getClass());
+    assertEquals("ctx", ((InvocationContextItem) result.get(27)).getVarName());
+  }
+
+  @Test
+  public void testParsePattern() {
+    String pattern = " %m  cs-uri-stem %{response-header}o ";
+    List<AccessLogItem<RoutingContext>> result = accessLogPatternParser.parsePattern(pattern);
+    assertEquals(7, result.size());
+
+    assertEquals(" ", result.get(0).getFormattedItem(null));
+    assertEquals(HttpMethodItem.class, result.get(1).getClass());
+    assertEquals("  ", result.get(2).getFormattedItem(null));
+    assertEquals(UrlPathItem.class, result.get(3).getClass());
+    assertEquals(" ", result.get(4).getFormattedItem(null));
+    assertEquals(ResponseHeaderItem.class, result.get(5).getClass());
+    assertEquals("response-header", ((ResponseHeaderItem) result.get(5)).getVarName());
+    assertEquals(" ", result.get(6).getFormattedItem(null));
+  }
+
+  @Test
+  public void testParsePatternWithNoBlank() {
+    String pattern = "%mcs-uri-stem%{response-header}o";
+    List<AccessLogItem<RoutingContext>> result = accessLogPatternParser.parsePattern(pattern);
+    assertEquals(3, result.size());
+
+    assertEquals(HttpMethodItem.class, result.get(0).getClass());
+    assertEquals(UrlPathItem.class, result.get(1).getClass());
+    assertEquals(ResponseHeaderItem.class, result.get(2).getClass());
+    assertEquals("response-header", ((ResponseHeaderItem) result.get(2)).getVarName());
+  }
+
+  @Test
+  public void testParsePatternComplex() {
+    String pattern = "%m  cs-uri-stem %{response-header}o abc cs-uri-query %s%{request} header}i plain cs-uri";
+    List<AccessLogItem<RoutingContext>> result = accessLogPatternParser.parsePattern(pattern);
+    assertEquals(12, result.size());
+
+    assertEquals(HttpMethodItem.class, result.get(0).getClass());
+    assertEquals("  ", result.get(1).getFormattedItem(null));
+    assertEquals(UrlPathItem.class, result.get(2).getClass());
+    assertEquals(" ", result.get(3).getFormattedItem(null));
+    assertEquals(ResponseHeaderItem.class, result.get(4).getClass());
+    assertEquals("response-header", ((ResponseHeaderItem) result.get(4)).getVarName());
+    assertEquals(" abc ", result.get(5).getFormattedItem(null));
+    assertEquals(QueryStringItem.class, result.get(6).getClass());
+    assertEquals(" ", result.get(7).getFormattedItem(null));
+    assertEquals(HttpStatusItem.class, result.get(8).getClass());
+    assertEquals(RequestHeaderItem.class, result.get(9).getClass());
+    assertEquals("request} header", ((RequestHeaderItem) result.get(9)).getVarName());
+    assertEquals(" plain ", result.get(10).getFormattedItem(null));
+    assertEquals(UrlPathWithQueryItem.class, result.get(11).getClass());
+  }
+
+  Comparator<VertxRestAccessLogItemMeta> comparator = VertxRestAccessLogPatternParser.accessLogItemMetaComparator;
+
+  /**
+   * one factor test
+   */
+  @Test
+  public void testCompareMetaSimple() {
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestAccessLogItemMeta(null, null, null, 0),
+            new VertxRestAccessLogItemMeta(null, null, null, 1)
+        ) < 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestAccessLogItemMeta(null, "}abc", null, 0),
+            new VertxRestAccessLogItemMeta(null, null, null, 0)
+        ) < 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestAccessLogItemMeta(null, "}abc", null, 0),
+            new VertxRestAccessLogItemMeta(null, "}de", null, 0)
+        ) < 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestAccessLogItemMeta(null, "}abc", null, 0),
+            new VertxRestAccessLogItemMeta(null, "}ab", null, 0)
+        ) < 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestAccessLogItemMeta("%abc", null, null, 0),
+            new VertxRestAccessLogItemMeta("%de", null, null, 0)
+        ) < 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestAccessLogItemMeta("%abc", null, null, 0),
+            new VertxRestAccessLogItemMeta("%ab", null, null, 0)
+        ) < 0
+    );
+    Assert.assertEquals(0, comparator.compare(
+        new VertxRestAccessLogItemMeta("%abc", null, null, 0),
+        new VertxRestAccessLogItemMeta("%abc", null, null, 0)
+    ));
+  }
+
+  /**
+   * multiple factors test
+   */
+  @Test
+  public void testCompareMetaComplex() {
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestAccessLogItemMeta("%bcd", "}ab", null, 0),
+            new VertxRestAccessLogItemMeta("%abc", "}abc", null, 0)
+        ) > 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestAccessLogItemMeta("%abc", null, null, 0),
+            new VertxRestAccessLogItemMeta("%bcd", "}ab", null, 0)
+        ) > 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestAccessLogItemMeta("%bcd", "}abc", null, 0),
+            new VertxRestAccessLogItemMeta("%abc", "}abc", null, 0)
+        ) > 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestAccessLogItemMeta("%abc", "}abc", null, 1),
+            new VertxRestAccessLogItemMeta("%ab", "}ab", null, 0)
+        ) > 0
+    );
+  }
+
+  @Test
+  public void testComparePlaceholderString() {
+    Assert.assertTrue(
+        VertxRestAccessLogPatternParser.comparePlaceholderString("abc", "bbc") < 0
+    );
+    Assert.assertTrue(
+        VertxRestAccessLogPatternParser.comparePlaceholderString("abc", "ab") < 0
+    );
+    Assert.assertEquals(0, VertxRestAccessLogPatternParser.comparePlaceholderString("abc", "abc"));
+    Assert.assertTrue(
+        VertxRestAccessLogPatternParser.comparePlaceholderString("bbc", "abc") > 0
+    );
+    Assert.assertTrue(
+        VertxRestAccessLogPatternParser.comparePlaceholderString("ab", "abc") > 0
+    );
+  }
+
+  @Test
+  public void testExtendedVertxRestAccessLogItemCreator() {
+    final List<VertxRestAccessLogItemMeta> metaList0 = new ArrayList<>();
+    metaList0.add(new VertxRestAccessLogItemMeta("%{", "}abc", null));
+    metaList0.add(new VertxRestAccessLogItemMeta("%{", "}a", null));
+    metaList0.add(new VertxRestAccessLogItemMeta("%_", null, null, -1));
+
+    final List<VertxRestAccessLogItemMeta> metaList1 = new ArrayList<>();
+    metaList0.add(new VertxRestAccessLogItemMeta("%a", "}abc", null));
+    metaList0.add(new VertxRestAccessLogItemMeta("%0", "}abc", null, 1));
+    metaList0.add(new VertxRestAccessLogItemMeta("%m", null, null));
+
+    new MockUp<VertxRestAccessLogPatternParser>() {
+      @Mock
+      List<VertxRestAccessLogItemMeta> loadVertxRestAccessLogItemMeta() {
+        List<VertxRestAccessLogItemMeta> metaList = new ArrayList<>(1);
+        CompositeVertxRestAccessLogItemMeta compositeMeta0 = new CompositeVertxRestAccessLogItemMeta() {
+          @Override
+          public List<VertxRestAccessLogItemMeta> getAccessLogItemMetas() {
+            return metaList0;
+          }
+        };
+        CompositeVertxRestAccessLogItemMeta compositeMeta1 = new CompositeVertxRestAccessLogItemMeta() {
+          @Override
+          public List<VertxRestAccessLogItemMeta> getAccessLogItemMetas() {
+            return metaList1;
+          }
+        };
+        metaList.add(compositeMeta0);
+        metaList.add(compositeMeta1);
+        metaList.add(new VertxRestAccessLogItemMeta("%{", null, null));
+        return metaList;
+      }
+    };
+
+    VertxRestAccessLogPatternParser parser = new VertxRestAccessLogPatternParser();
+
+    List<VertxRestAccessLogItemMeta> accessLogItemMetaList =
+        Deencapsulation.getField(parser, "metaList");
+
+    assertEquals(7, accessLogItemMetaList.size());
+    assertEquals("%_", accessLogItemMetaList.get(0).getPrefix());
+    assertEquals("%a", accessLogItemMetaList.get(1).getPrefix());
+    assertEquals("}abc", accessLogItemMetaList.get(1).getSuffix());
+    assertEquals("%{", accessLogItemMetaList.get(2).getPrefix());
+    assertEquals("}abc", accessLogItemMetaList.get(2).getSuffix());
+    assertEquals("%{", accessLogItemMetaList.get(3).getPrefix());
+    assertEquals("}a", accessLogItemMetaList.get(3).getSuffix());
+    assertEquals("%m", accessLogItemMetaList.get(4).getPrefix());
+    assertNull(accessLogItemMetaList.get(4).getSuffix());
+    assertEquals("%{", accessLogItemMetaList.get(5).getPrefix());
+    assertNull(accessLogItemMetaList.get(5).getSuffix());
+    assertEquals("%0", accessLogItemMetaList.get(6).getPrefix());
+    assertEquals("}abc", accessLogItemMetaList.get(6).getSuffix());
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/matcher/PercentagePrefixConfigurableMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/matcher/PercentagePrefixConfigurableMatcherTest.java
deleted file mode 100644
index d048999a5..000000000
--- a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/matcher/PercentagePrefixConfigurableMatcherTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.servicecomb.transport.rest.vertx.accesslog.parser.matcher;
-
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemLocation;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.placeholder.AccessLogItemTypeEnum;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class PercentagePrefixConfigurableMatcherTest {
-
-  public static final PercentagePrefixConfigurableMatcher MATCHER = new PercentagePrefixConfigurableMatcher();
-
-  public static final String TEST_RAW_PATTERN = "%{pattern}t %{test pattern}C %{test pattern}t %{test pattern}SCB-ctx";
-
-  @Test
-  public void testMatch() {
-    AccessLogItemLocation location;
-    location = MATCHER.match(TEST_RAW_PATTERN, 0);
-    Assert.assertEquals(
-        location,
-        new AccessLogItemLocation()
-            .setStart(0)
-            .setEnd(11)
-            .setPlaceHolder(AccessLogItemTypeEnum.DATETIME_CONFIGURABLE));
-
-    location = MATCHER.match(TEST_RAW_PATTERN, 10);
-    Assert.assertEquals(
-        location,
-        new AccessLogItemLocation()
-            .setStart(12)
-            .setEnd(28)
-            .setPlaceHolder(AccessLogItemTypeEnum.COOKIE));
-
-    location = MATCHER.match(TEST_RAW_PATTERN, 30);
-    Assert.assertEquals(
-        location,
-        new AccessLogItemLocation()
-            .setStart(46)
-            .setEnd(68)
-            .setPlaceHolder(AccessLogItemTypeEnum.SCB_INVOCATION_CONTEXT));
-
-    location = MATCHER.match(TEST_RAW_PATTERN, 47);
-    Assert.assertNull(location);
-  }
-
-  @Test
-  public void testNotMatch() {
-    AccessLogItemLocation location = MATCHER.match("notmatch", 0);
-    Assert.assertNull(location);
-  }
-
-  @Test
-  public void testNotMatchWithPrefix() {
-    AccessLogItemLocation location = MATCHER.match("%{notmatch}x", 0);
-    Assert.assertNull(location);
-  }
-}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/matcher/SimpleItemMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/matcher/SimpleItemMatcherTest.java
deleted file mode 100644
index 3ce57ae16..000000000
--- a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/accesslog/parser/matcher/SimpleItemMatcherTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.servicecomb.transport.rest.vertx.accesslog.parser.matcher;
-
-import org.apache.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogItemLocation;
-import org.apache.servicecomb.transport.rest.vertx.accesslog.placeholder.AccessLogItemTypeEnum;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class SimpleItemMatcherTest {
-
-  private static final SimpleItemMatcher MATCHER = new SimpleItemMatcher();
-
-  public static final String PATTERN = "%h - - %t %r %s %B";
-
-  @Test
-  public void testMatch() {
-    AccessLogItemLocation location = MATCHER.match(PATTERN, 0);
-    Assert.assertEquals(
-        location,
-        new AccessLogItemLocation()
-            .setStart(0)
-            .setEnd(2)
-            .setPlaceHolder(AccessLogItemTypeEnum.REMOTE_HOSTNAME));
-
-    location = MATCHER.match(PATTERN, 3);
-    Assert.assertEquals(
-        location,
-        new AccessLogItemLocation()
-            .setStart(7)
-            .setEnd(9)
-            .setPlaceHolder(AccessLogItemTypeEnum.DATETIME_DEFAULT));
-
-    location = MATCHER.match(PATTERN, 17);
-    Assert.assertNull(location);
-  }
-
-  @Test
-  public void testNotMatch() {
-    AccessLogItemLocation location = MATCHER.match("notmatch", 0);
-    Assert.assertNull(location);
-  }
-}
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.accesslog.parser.VertxRestAccessLogItemMeta b/transports/transport-rest/transport-rest-vertx/src/test/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.accesslog.parser.VertxRestAccessLogItemMeta
new file mode 100644
index 000000000..b539f7c7d
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.accesslog.parser.VertxRestAccessLogItemMeta
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.apache.servicecomb.transport.rest.vertx.accesslog.parser.impl.TestSingleExtendedAccessLogItemMeta
+org.apache.servicecomb.transport.rest.vertx.accesslog.parser.impl.TestCompositeExtendedAccessLogItemMeta
\ No newline at end of file


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services