You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by ni...@apache.org on 2017/12/22 12:28:18 UTC

[incubator-servicecomb-java-chassis] 01/09: [JAV-589] provide access log handler component

This is an automated email from the ASF dual-hosted git repository.

ningjiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git

commit 65d8bca932086d9fcdc84a23781c647cfb387b96
Author: yaohaishi <ya...@huawei.com>
AuthorDate: Fri Dec 15 17:37:45 2017 +0800

    [JAV-589] provide access log handler component
---
 .../transport/rest/vertx/RestServerVerticle.java   |  17 +++
 .../vertx/accesslog/AccessLogConfiguration.java    |  38 +++++++
 .../rest/vertx/accesslog/AccessLogHandler.java     |   8 ++
 .../rest/vertx/accesslog/AccessLogParam.java       |  48 +++++++++
 .../vertx/accesslog/element/AccessLogElement.java  |  16 +++
 .../element/impl/BytesWrittenV1Element.java        |  23 ++++
 .../element/impl/BytesWrittenV2Element.java        |  25 +++++
 .../accesslog/element/impl/CookieElement.java      |  55 ++++++++++
 .../element/impl/DatetimeConfigurableElement.java  |  83 ++++++++++++++
 .../element/impl/DurationMillisecondElement.java   |  11 ++
 .../element/impl/DurationSecondElement.java        |  11 ++
 .../element/impl/FirstLineOfRequestElement.java    |  24 +++++
 .../accesslog/element/impl/LocalHostElement.java   |  33 ++++++
 .../accesslog/element/impl/LocalPortElement.java   |  26 +++++
 .../accesslog/element/impl/MethodElement.java      |  28 +++++
 .../accesslog/element/impl/PlainTextElement.java   |  20 ++++
 .../accesslog/element/impl/QueryOnlyElement.java   |  26 +++++
 .../accesslog/element/impl/RemoteHostElement.java  |  32 ++++++
 .../element/impl/RequestHeaderElement.java         |  36 +++++++
 .../element/impl/ResponseHeaderElement.java        |  41 +++++++
 .../accesslog/element/impl/StatusElement.java      |  20 ++++
 .../element/impl/UriPathIncludeQueryElement.java   |  27 +++++
 .../accesslog/element/impl/UriPathOnlyElement.java |  26 +++++
 .../element/impl/VersionOrProtocolElement.java     |  36 +++++++
 .../vertx/accesslog/impl/AccessLogHandlerImpl.java |  54 ++++++++++
 .../parser/AccessLogElementExtraction.java         |  60 +++++++++++
 .../accesslog/parser/AccessLogPatternParser.java   |   7 ++
 .../parser/impl/DefaultAccessLogPatternParser.java | 112 +++++++++++++++++++
 .../parser/matcher/AccessLogElementMatcher.java    |  18 ++++
 .../parser/matcher/impl/BytesWrittenV1Matcher.java |  19 ++++
 .../parser/matcher/impl/BytesWrittenV2Matcher.java |  19 ++++
 .../impl/ConfigurableAccessLogElementMatcher.java  |  56 ++++++++++
 .../parser/matcher/impl/CookieElementMatcher.java  |  25 +++++
 .../matcher/impl/DatetimeConfigurableMatcher.java  |  34 ++++++
 .../parser/matcher/impl/DatetimeMatcher.java       |  19 ++++
 .../matcher/impl/DurationMillisecondMatcher.java   |  19 ++++
 .../parser/matcher/impl/DurationSecondMatcher.java |  19 ++++
 .../matcher/impl/FirstLineOfRequestMatcher.java    |  19 ++++
 .../impl/ImmutableAccessLogElementMatcher.java     |  36 +++++++
 .../parser/matcher/impl/LocalHostMatcher.java      |  19 ++++
 .../parser/matcher/impl/LocalPortMatcher.java      |  19 ++++
 .../parser/matcher/impl/MethodMatcher.java         |  20 ++++
 .../impl/MultiPatternImmutableElementMatcher.java  |  22 ++++
 .../parser/matcher/impl/QueryOnlyMatcher.java      |  19 ++++
 .../parser/matcher/impl/RemoteHostMatcher.java     |  19 ++++
 .../matcher/impl/RequestHeaderElementMatcher.java  |  25 +++++
 .../matcher/impl/ResponseHeaderElementMatcher.java |  25 +++++
 .../impl/SinglePatternImmutableElementMatcher.java |  20 ++++
 .../parser/matcher/impl/StatusMatcher.java         |  19 ++++
 .../matcher/impl/UriPathIncludeQueryMatcher.java   |  55 ++++++++++
 .../parser/matcher/impl/UriPathOnlyMatcher.java    |  19 ++++
 .../matcher/impl/VersionOrProtocolMatcher.java     |  19 ++++
 .../main/resources/config/base/log4j.properties    |  13 +++
 .../element/impl/BytesWrittenV1ElementTest.java    |  63 +++++++++++
 .../element/impl/BytesWrittenV2ElementTest.java    |  61 +++++++++++
 .../accesslog/element/impl/CookieElementTest.java  |  86 +++++++++++++++
 .../impl/DatetimeConfigurableElementTest.java      | 101 +++++++++++++++++
 .../impl/DurationMillisecondElementTest.java       |  21 ++++
 .../element/impl/DurationSecondElementTest.java    |  41 +++++++
 .../impl/FirstLineOfRequestElementTest.java        |  36 +++++++
 .../element/impl/LocalHostElementTest.java         | 100 +++++++++++++++++
 .../element/impl/LocalPortElementTest.java         |  60 +++++++++++
 .../accesslog/element/impl/MethodElementTest.java  |  46 ++++++++
 .../element/impl/PlainTextElementTest.java         |  14 +++
 .../element/impl/QueryOnlyElementTest.java         |  73 +++++++++++++
 .../element/impl/RemoteHostElementTest.java        | 101 +++++++++++++++++
 .../element/impl/RequestHeaderElementTest.java     |  66 ++++++++++++
 .../element/impl/ResponseHeaderElementTest.java    |  85 +++++++++++++++
 .../accesslog/element/impl/StatusElementTest.java  |  45 ++++++++
 .../impl/UriPathIncludeQueryElementTest.java       |  78 ++++++++++++++
 .../element/impl/UriPathOnlyElementTest.java       |  58 ++++++++++
 .../element/impl/VersionOrProtocolElementTest.java |  58 ++++++++++
 .../accesslog/impl/AccessLogHandlerImplTest.java   |  72 +++++++++++++
 .../impl/DefaultAccessLogPatternParserTest.java    |  90 ++++++++++++++++
 .../matcher/impl/BytesWrittenV1MatcherTest.java    |  46 ++++++++
 .../matcher/impl/BytesWrittenV2MatcherTest.java    |  46 ++++++++
 .../matcher/impl/CookieElementMatcherTest.java     |  48 +++++++++
 .../impl/DatetimeConfigurableMatcherTest.java      | 120 +++++++++++++++++++++
 .../parser/matcher/impl/DatetimeMatcherTest.java   |  46 ++++++++
 .../impl/DurationMillisecondMatcherTest.java       |  46 ++++++++
 .../matcher/impl/DurationSecondMatcherTest.java    |  46 ++++++++
 .../impl/FirstLineOfRequestMatcherTest.java        |  46 ++++++++
 .../impl/ImmutableAccessLogElementMatcherTest.java |  64 +++++++++++
 .../parser/matcher/impl/LocalHostMatcherTest.java  |  46 ++++++++
 .../parser/matcher/impl/LocalPortMatcherTest.java  |  46 ++++++++
 .../parser/matcher/impl/MethodMatcherTest.java     |  30 ++++++
 .../parser/matcher/impl/QueryOnlyMatcherTest.java  |  26 +++++
 .../parser/matcher/impl/RemoteHostMatcherTest.java |  49 +++++++++
 .../impl/RequestHeaderElementMatcherTest.java      |  47 ++++++++
 .../impl/ResponseHeaderElementMatcherTest.java     |  48 +++++++++
 .../parser/matcher/impl/StatusMatcherTest.java     |  24 +++++
 .../impl/UriPathIncludeQueryMatcherTest.java       |  35 ++++++
 .../matcher/impl/UriPathOnlyMatcherTest.java       |  26 +++++
 .../matcher/impl/VersionOrProtocolMatcherTest.java |  49 +++++++++
 94 files changed, 3828 insertions(+)

diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/RestServerVerticle.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/RestServerVerticle.java
index 796ea87..476a833 100644
--- a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/RestServerVerticle.java
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/RestServerVerticle.java
@@ -30,6 +30,9 @@ import io.servicecomb.foundation.ssl.SSLCustom;
 import io.servicecomb.foundation.ssl.SSLOption;
 import io.servicecomb.foundation.ssl.SSLOptionFactory;
 import io.servicecomb.foundation.vertx.VertxTLSBuilder;
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogConfiguration;
+import io.servicecomb.transport.rest.vertx.accesslog.impl.AccessLogHandlerImpl;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.impl.DefaultAccessLogPatternParser;
 import io.vertx.core.AbstractVerticle;
 import io.vertx.core.Context;
 import io.vertx.core.Future;
@@ -37,6 +40,8 @@ import io.vertx.core.Vertx;
 import io.vertx.core.http.HttpServer;
 import io.vertx.core.http.HttpServerOptions;
 import io.vertx.ext.web.Router;
+import io.vertx.ext.web.handler.LoggerFormat;
+import io.vertx.ext.web.handler.impl.LoggerHandlerImpl;
 
 public class RestServerVerticle extends AbstractVerticle {
   private static final Logger LOGGER = LoggerFactory.getLogger(RestServerVerticle.class);
@@ -72,6 +77,7 @@ public class RestServerVerticle extends AbstractVerticle {
     }
 
     Router mainRouter = Router.router(vertx);
+    mountAccessLogHandler(mainRouter);
     initDispatcher(mainRouter);
 
     HttpServer httpServer = createHttpServer();
@@ -80,6 +86,17 @@ public class RestServerVerticle extends AbstractVerticle {
     startListen(httpServer, startFuture);
   }
 
+  private void mountAccessLogHandler(Router mainRouter) {
+    if (AccessLogConfiguration.INSTANCE.getAccessLogEnabled()) {
+      String pattern = AccessLogConfiguration.INSTANCE.getAccesslogPattern();
+      LOGGER.info("access log enabled, pattern = {}", pattern);
+      mainRouter.route()
+          .handler(new AccessLogHandlerImpl(
+              pattern,
+              new DefaultAccessLogPatternParser()));
+    }
+  }
+
   private void initDispatcher(Router mainRouter) {
     List<VertxHttpDispatcher> dispatchers = SPIServiceUtils.getSortedService(VertxHttpDispatcher.class);
     for (VertxHttpDispatcher dispatcher : dispatchers) {
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/AccessLogConfiguration.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/AccessLogConfiguration.java
new file mode 100644
index 0000000..af6e852
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/AccessLogConfiguration.java
@@ -0,0 +1,38 @@
+package io.servicecomb.transport.rest.vertx.accesslog;
+
+import com.netflix.config.DynamicPropertyFactory;
+
+import io.servicecomb.core.BootListener.BootEvent;
+
+public final class AccessLogConfiguration {
+  private static final String BASE = "cse.accesslog.";
+
+  private static final String ACCESSLOG_ENABLED = BASE + "enabled";
+
+  private static final String ACCESSLOG_PATTERN = BASE + "pattern";
+
+  public static final AccessLogConfiguration INSTANCE = new AccessLogConfiguration();
+
+  private AccessLogConfiguration() {
+
+  }
+
+  public boolean getAccessLogEnabled() {
+    String enabled = getProperty("false", ACCESSLOG_ENABLED);
+    return Boolean.parseBoolean(enabled);
+  }
+
+  public String getAccesslogPattern() {
+    String pattern = getProperty("%h - - %t %r %s %B", ACCESSLOG_PATTERN);
+    return pattern;
+  }
+
+  private String getProperty(String defaultValue, String key) {
+    String property = DynamicPropertyFactory.getInstance().getStringProperty(key, defaultValue).get();
+    if (null == property) {
+      return defaultValue;
+    } else {
+      return property;
+    }
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/AccessLogHandler.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/AccessLogHandler.java
new file mode 100644
index 0000000..10da027
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/AccessLogHandler.java
@@ -0,0 +1,8 @@
+package io.servicecomb.transport.rest.vertx.accesslog;
+
+import io.vertx.core.Handler;
+import io.vertx.ext.web.RoutingContext;
+
+public interface AccessLogHandler extends Handler<RoutingContext> {
+
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/AccessLogParam.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/AccessLogParam.java
new file mode 100644
index 0000000..337f27c
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/AccessLogParam.java
@@ -0,0 +1,48 @@
+package io.servicecomb.transport.rest.vertx.accesslog;
+
+import io.vertx.ext.web.RoutingContext;
+
+public class AccessLogParam {
+  private RoutingContext routingContext;
+
+  private long startMillisecond;
+
+  private long endMillisecond;
+
+  public RoutingContext getRoutingContext() {
+    return routingContext;
+  }
+
+  public AccessLogParam setRoutingContext(RoutingContext routingContext) {
+    this.routingContext = routingContext;
+    return this;
+  }
+
+  public long getStartMillisecond() {
+    return startMillisecond;
+  }
+
+  public AccessLogParam setStartMillisecond(long startMillisecond) {
+    this.startMillisecond = startMillisecond;
+    return this;
+  }
+
+  public long getEndMillisecond() {
+    return endMillisecond;
+  }
+
+  public AccessLogParam setEndMillisecond(long endMillisecond) {
+    this.endMillisecond = endMillisecond;
+    return this;
+  }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb = new StringBuilder("AccessLogParam{");
+    sb.append("routingContext=").append(routingContext);
+    sb.append(", startMillisecond=").append(startMillisecond);
+    sb.append(", endMillisecond=").append(endMillisecond);
+    sb.append('}');
+    return sb.toString();
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/AccessLogElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/AccessLogElement.java
new file mode 100644
index 0000000..8a562e8
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/AccessLogElement.java
@@ -0,0 +1,16 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+
+/**
+ * element should be printed into access log.
+ */
+public interface AccessLogElement {
+  /**
+   * find out specified content from {@link AccessLogParam}, format the content and return it.
+   *
+   * @param accessLogParam
+   * @return
+   */
+  String getFormattedElement(AccessLogParam accessLogParam);
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/BytesWrittenV1Element.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/BytesWrittenV1Element.java
new file mode 100644
index 0000000..374f5f8
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/BytesWrittenV1Element.java
@@ -0,0 +1,23 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.ext.web.RoutingContext;
+
+public class BytesWrittenV1Element implements AccessLogElement {
+
+  public static final String ZERO_BYTES = "0";
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    HttpServerResponse response = accessLogParam.getRoutingContext().response();
+    if (null == response) {
+      return ZERO_BYTES;
+    }
+
+    long bytesWritten = response.bytesWritten();
+
+    return String.valueOf(bytesWritten);
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/BytesWrittenV2Element.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/BytesWrittenV2Element.java
new file mode 100644
index 0000000..675405d
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/BytesWrittenV2Element.java
@@ -0,0 +1,25 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.vertx.core.http.HttpServerResponse;
+
+public class BytesWrittenV2Element implements AccessLogElement {
+
+  public static final String ZERO_BYTES = "-";
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    HttpServerResponse response = accessLogParam.getRoutingContext().response();
+    if (null == response) {
+      return ZERO_BYTES;
+    }
+
+    long bytesWritten = response.bytesWritten();
+    if (0 == bytesWritten) {
+      return ZERO_BYTES;
+    } else {
+      return String.valueOf(bytesWritten);
+    }
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/CookieElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/CookieElement.java
new file mode 100644
index 0000000..a727ab5
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/CookieElement.java
@@ -0,0 +1,55 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import java.util.Set;
+
+import com.sun.org.apache.regexp.internal.RE;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.vertx.ext.web.Cookie;
+import io.vertx.ext.web.RoutingContext;
+
+public class CookieElement implements AccessLogElement {
+
+  public static final String RESULT_NOT_FOUND = "-";
+
+  private final String identifier;
+
+  public CookieElement(String identifier) {
+    this.identifier = identifier;
+  }
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    RoutingContext context = accessLogParam.getRoutingContext();
+    if (null == context) {
+      return RESULT_NOT_FOUND;
+    }
+
+    if (context.cookieCount() == 0) {
+      return RESULT_NOT_FOUND;
+    }
+
+    Set<Cookie> cookieSet = context.cookies();
+    if (null == cookieSet) {
+      return RESULT_NOT_FOUND;
+    }
+
+    String result = null;
+    for (Cookie cookie : cookieSet) {
+      if (identifier.equals(cookie.getName())) {
+        result = cookie.getValue();
+      }
+    }
+
+    if (null == result) {
+      return RESULT_NOT_FOUND;
+    }
+
+    return result;
+  }
+
+  public String getIdentifier() {
+    return identifier;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DatetimeConfigurableElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DatetimeConfigurableElement.java
new file mode 100644
index 0000000..f30e61a
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DatetimeConfigurableElement.java
@@ -0,0 +1,83 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.springframework.util.StringUtils;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+
+public class DatetimeConfigurableElement implements AccessLogElement {
+  private String pattern;
+
+  private TimeZone timezone;
+
+  private Locale locale;
+
+  private final ThreadLocal<SimpleDateFormat> datetimeFormatHolder = new ThreadLocal<>();
+
+  public DatetimeConfigurableElement() {
+    this("||");
+  }
+
+  public DatetimeConfigurableElement(String config) {
+    String[] configArr = null;
+    if (config.contains("|")) {
+      configArr = splitConfig(config);
+    } else {
+      // if there is no seperator "|", regard config as pattern.
+      configArr = new String[3];
+      configArr[0] = config;
+    }
+    if (3 != configArr.length) {
+      throw new IllegalArgumentException(
+          "wrong format of configuration, \"PATTERN|TIMEZONE|LOCALE\" is expected, but actually is \"" + config + "\"");
+    }
+
+    setConfigruations(configArr);
+  }
+
+  protected String[] splitConfig(String config) {
+    return config.split("\\|{1}?", -1);
+  }
+
+  private void setConfigruations(String[] configArr) {
+    this.pattern = StringUtils.isEmpty(configArr[0]) ? "EEE, dd MMM yyyy HH:mm:ss zzz" : configArr[0];
+    this.timezone = StringUtils.isEmpty(configArr[1]) ? TimeZone.getDefault() : TimeZone.getTimeZone(configArr[1]);
+    this.locale = StringUtils.isEmpty(configArr[2]) ? Locale.US : Locale.forLanguageTag(configArr[2]);
+  }
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    SimpleDateFormat dateFormat = getDatetimeFormat();
+    String datetime = dateFormat.format(new Date(accessLogParam.getStartMillisecond()));
+    return datetime;
+  }
+
+  private SimpleDateFormat getDatetimeFormat() {
+    SimpleDateFormat dateFormat = datetimeFormatHolder.get();
+    if (null == dateFormat) {
+      dateFormat = new SimpleDateFormat(pattern, locale);
+      dateFormat.setTimeZone(timezone);
+
+      datetimeFormatHolder.set(dateFormat);
+    }
+
+    return dateFormat;
+  }
+
+  public String getPattern() {
+    return pattern;
+  }
+
+  public TimeZone getTimezone() {
+    return timezone;
+  }
+
+  public Locale getLocale() {
+    return locale;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DurationMillisecondElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DurationMillisecondElement.java
new file mode 100644
index 0000000..854fcd9
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DurationMillisecondElement.java
@@ -0,0 +1,11 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+
+public class DurationMillisecondElement implements AccessLogElement {
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    return String.valueOf(accessLogParam.getEndMillisecond() - accessLogParam.getStartMillisecond());
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DurationSecondElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DurationSecondElement.java
new file mode 100644
index 0000000..7a995f4
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DurationSecondElement.java
@@ -0,0 +1,11 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+
+public class DurationSecondElement implements AccessLogElement {
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    return String.valueOf((accessLogParam.getEndMillisecond() - accessLogParam.getStartMillisecond()) / 1000);
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/FirstLineOfRequestElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/FirstLineOfRequestElement.java
new file mode 100644
index 0000000..6633817
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/FirstLineOfRequestElement.java
@@ -0,0 +1,24 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+
+public class FirstLineOfRequestElement implements AccessLogElement {
+  private static final MethodElement METHOD_ELEMENT = new MethodElement();
+
+  private static final UriPathOnlyElement URI_PATH_ONLY_ELEMENT = new UriPathOnlyElement();
+
+  private static final VersionOrProtocolElement VERSION_OR_PROTOCOL_ELEMENT = new VersionOrProtocolElement();
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    StringBuilder result = new StringBuilder()
+        .append(METHOD_ELEMENT.getFormattedElement(accessLogParam))
+        .append(" \"")
+        .append(URI_PATH_ONLY_ELEMENT.getFormattedElement(accessLogParam))
+        .append("\" ")
+        .append(VERSION_OR_PROTOCOL_ELEMENT.getFormattedElement(accessLogParam));
+
+    return result.toString();
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/LocalHostElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/LocalHostElement.java
new file mode 100644
index 0000000..18d4161
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/LocalHostElement.java
@@ -0,0 +1,33 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import org.springframework.util.StringUtils;
+
+import io.netty.channel.local.LocalAddress;
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.net.SocketAddress;
+
+public class LocalHostElement implements AccessLogElement {
+
+  public static final String EMPTY_RESULT = "-";
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    HttpServerRequest request = accessLogParam.getRoutingContext().request();
+    if (null == request) {
+      return EMPTY_RESULT;
+    }
+
+    SocketAddress localAddress = request.localAddress();
+    if (null == localAddress) {
+      return EMPTY_RESULT;
+    }
+
+    String localHost = localAddress.host();
+    if (StringUtils.isEmpty(localHost)) {
+      return EMPTY_RESULT;
+    }
+    return localHost;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/LocalPortElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/LocalPortElement.java
new file mode 100644
index 0000000..8bb0a60
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/LocalPortElement.java
@@ -0,0 +1,26 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.net.SocketAddress;
+
+public class LocalPortElement implements AccessLogElement {
+
+  public static final String EMPTY_RESULT = "-";
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    HttpServerRequest request = accessLogParam.getRoutingContext().request();
+    if (null == request) {
+      return EMPTY_RESULT;
+    }
+
+    SocketAddress localAddress = request.localAddress();
+    if (null == localAddress) {
+      return EMPTY_RESULT;
+    }
+
+    return String.valueOf(localAddress.port());
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/MethodElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/MethodElement.java
new file mode 100644
index 0000000..54baa04
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/MethodElement.java
@@ -0,0 +1,28 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.vertx.core.http.HttpMethod;
+import io.vertx.core.http.HttpServerRequest;
+
+/**
+ * HTTP method
+ */
+public class MethodElement implements AccessLogElement {
+
+  public static final String EMPTY_RESULT = "-";
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    HttpServerRequest request = accessLogParam.getRoutingContext().request();
+    if (null == request) {
+      return EMPTY_RESULT;
+    }
+
+    HttpMethod method = request.method();
+    if (null == method) {
+      return EMPTY_RESULT;
+    }
+    return method.toString();
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/PlainTextElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/PlainTextElement.java
new file mode 100644
index 0000000..99cb9f9
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/PlainTextElement.java
@@ -0,0 +1,20 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+
+/**
+ * Print content as it is.
+ */
+public class PlainTextElement implements AccessLogElement {
+  private final String content;
+
+  public PlainTextElement(String content) {
+    this.content = content;
+  }
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    return content;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/QueryOnlyElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/QueryOnlyElement.java
new file mode 100644
index 0000000..abd6fb6
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/QueryOnlyElement.java
@@ -0,0 +1,26 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import org.springframework.util.StringUtils;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.vertx.core.http.HttpServerRequest;
+
+public class QueryOnlyElement implements AccessLogElement {
+
+  public static final String EMPTY_RESULT = "-";
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    HttpServerRequest request = accessLogParam.getRoutingContext().request();
+    if (null == request) {
+      return EMPTY_RESULT;
+    }
+
+    String query = request.query();
+    if (StringUtils.isEmpty(query)) {
+      return EMPTY_RESULT;
+    }
+    return query;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/RemoteHostElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/RemoteHostElement.java
new file mode 100644
index 0000000..0eef3c5
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/RemoteHostElement.java
@@ -0,0 +1,32 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import org.springframework.util.StringUtils;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.net.SocketAddress;
+
+public class RemoteHostElement implements AccessLogElement {
+
+  public static final String EMPTY_RESULT = "-";
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    HttpServerRequest request = accessLogParam.getRoutingContext().request();
+    if (null == request) {
+      return EMPTY_RESULT;
+    }
+
+    SocketAddress remoteAddress = request.remoteAddress();
+    if (null == remoteAddress) {
+      return EMPTY_RESULT;
+    }
+
+    String remoteHost = remoteAddress.host();
+    if (StringUtils.isEmpty(remoteHost)) {
+      return EMPTY_RESULT;
+    }
+    return remoteHost;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/RequestHeaderElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/RequestHeaderElement.java
new file mode 100644
index 0000000..d4bfd76
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/RequestHeaderElement.java
@@ -0,0 +1,36 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.vertx.core.MultiMap;
+
+public class RequestHeaderElement implements AccessLogElement {
+
+  public static final String RESULT_NOT_FOUND = "-";
+
+  private final String identifier;
+
+  public RequestHeaderElement(String identifier) {
+    this.identifier = identifier;
+  }
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    MultiMap headers = accessLogParam.getRoutingContext().request().headers();
+    if (null == headers) {
+      return "-";
+    }
+
+    String result = headers.get(identifier);
+
+    if (null == result) {
+      return RESULT_NOT_FOUND;
+    }
+
+    return result;
+  }
+
+  public String getIdentifier() {
+    return identifier;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/ResponseHeaderElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/ResponseHeaderElement.java
new file mode 100644
index 0000000..35bc722
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/ResponseHeaderElement.java
@@ -0,0 +1,41 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.vertx.core.MultiMap;
+import io.vertx.core.http.HttpServerResponse;
+
+public class ResponseHeaderElement implements AccessLogElement {
+
+  public static final String RESULT_NOT_FOUND = "-";
+
+  private final String identifier;
+
+  public ResponseHeaderElement(String identifier) {
+    this.identifier = identifier;
+  }
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    HttpServerResponse response = accessLogParam.getRoutingContext().response();
+    if (null == response) {
+      return RESULT_NOT_FOUND;
+    }
+
+    MultiMap headers = response.headers();
+    if (null == headers) {
+      return RESULT_NOT_FOUND;
+    }
+
+    String result = headers.get(identifier);
+    if (null == result) {
+      return RESULT_NOT_FOUND;
+    }
+
+    return result;
+  }
+
+  public String getIdentifier() {
+    return identifier;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/StatusElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/StatusElement.java
new file mode 100644
index 0000000..0702a22
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/StatusElement.java
@@ -0,0 +1,20 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.vertx.core.http.HttpServerResponse;
+
+public class StatusElement implements AccessLogElement {
+
+  public static final String EMPTY_RESULT = "-";
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    HttpServerResponse response = accessLogParam.getRoutingContext().response();
+    if (null == response) {
+      return EMPTY_RESULT;
+    }
+
+    return String.valueOf(response.getStatusCode());
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/UriPathIncludeQueryElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/UriPathIncludeQueryElement.java
new file mode 100644
index 0000000..baeb484
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/UriPathIncludeQueryElement.java
@@ -0,0 +1,27 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import org.springframework.util.StringUtils;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.vertx.core.http.HttpServerRequest;
+
+public class UriPathIncludeQueryElement implements AccessLogElement {
+
+  public static final String EMPTY_RESULT = "-";
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    HttpServerRequest request = accessLogParam.getRoutingContext().request();
+    if (null == request) {
+      return EMPTY_RESULT;
+    }
+
+    String uri = request.uri();
+    if (StringUtils.isEmpty(uri)) {
+      return EMPTY_RESULT;
+    }
+
+    return uri;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/UriPathOnlyElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/UriPathOnlyElement.java
new file mode 100644
index 0000000..3f0de40
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/UriPathOnlyElement.java
@@ -0,0 +1,26 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.vertx.core.http.HttpServerRequest;
+
+public class UriPathOnlyElement implements AccessLogElement {
+
+  public static final String EMPTY_RESULT = "-";
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    HttpServerRequest request = accessLogParam.getRoutingContext().request();
+
+    if (null == request) {
+      return EMPTY_RESULT;
+    }
+
+    String uri = request.path();
+    if (null == uri) {
+      return EMPTY_RESULT;
+    }
+
+    return uri;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/VersionOrProtocolElement.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/VersionOrProtocolElement.java
new file mode 100644
index 0000000..6196635
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/VersionOrProtocolElement.java
@@ -0,0 +1,36 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.http.HttpVersion;
+
+public class VersionOrProtocolElement implements AccessLogElement {
+
+  public static final String EMPTY_RESULT = "-";
+
+  @Override
+  public String getFormattedElement(AccessLogParam accessLogParam) {
+    HttpServerRequest request = accessLogParam.getRoutingContext().request();
+    if (null == request) {
+      return EMPTY_RESULT;
+    }
+    if (null == request.version()) {
+      return EMPTY_RESULT;
+    }
+    return getStringVersion(request.version());
+  }
+
+  private String getStringVersion(HttpVersion version) {
+    switch (version) {
+      case HTTP_2:
+        return "HTTP/2.0";
+      case HTTP_1_0:
+        return "HTTP/1.0";
+      case HTTP_1_1:
+        return "HTTP/1.1";
+      default:
+        return EMPTY_RESULT;
+    }
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/impl/AccessLogHandlerImpl.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/impl/AccessLogHandlerImpl.java
new file mode 100644
index 0000000..daba806
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/impl/AccessLogHandlerImpl.java
@@ -0,0 +1,54 @@
+package io.servicecomb.transport.rest.vertx.accesslog.impl;
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogHandler;
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogPatternParser;
+import io.vertx.ext.web.RoutingContext;
+
+public class AccessLogHandlerImpl implements AccessLogHandler {
+  private static Logger LOGGER = LoggerFactory.getLogger("accesslog");
+
+  private static AccessLogElement[] accessLogElements;
+
+  public AccessLogHandlerImpl(String rowLogPattern, AccessLogPatternParser accessLogPatternParser) {
+    List<AccessLogElementExtraction> extractionList = accessLogPatternParser.parsePattern(rowLogPattern);
+
+    accessLogElements = new AccessLogElement[extractionList.size()];
+    for (int i = 0; i < extractionList.size(); ++i) {
+      accessLogElements[i] = extractionList.get(i).getAccessLogElement();
+    }
+  }
+
+  @Override
+  public void handle(RoutingContext context) {
+    AccessLogParam accessLogParam = new AccessLogParam().setStartMillisecond(System.currentTimeMillis())
+        .setRoutingContext(context);
+
+    context.addBodyEndHandler(v -> log(accessLogParam));
+
+    context.next();
+  }
+
+  private void log(AccessLogParam accessLogParam) {
+    StringBuilder log = new StringBuilder(128);
+    accessLogParam.setEndMillisecond(System.currentTimeMillis());
+
+    AccessLogElement[] accessLogElements = getAccessLogElements();
+    for (int i = 0; i < accessLogElements.length; ++i) {
+      log.append(accessLogElements[i].getFormattedElement(accessLogParam));
+    }
+
+    LOGGER.info(log.toString());
+  }
+
+  private AccessLogElement[] getAccessLogElements() {
+    return accessLogElements;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogElementExtraction.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogElementExtraction.java
new file mode 100644
index 0000000..1e3eeb9
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogElementExtraction.java
@@ -0,0 +1,60 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+
+public class AccessLogElementExtraction {
+  private int start;
+
+  private int end;
+
+  private AccessLogElement accessLogElement;
+
+  public AccessLogElementExtraction() {
+
+  }
+
+  public AccessLogElementExtraction(int start, int end,
+      AccessLogElement accessLogElement) {
+    this.start = start;
+    this.end = end;
+    this.accessLogElement = accessLogElement;
+  }
+
+  public int getStart() {
+    return start;
+  }
+
+  public AccessLogElementExtraction setStart(int start) {
+    this.start = start;
+    return this;
+  }
+
+  public int getEnd() {
+    return end;
+  }
+
+  public AccessLogElementExtraction setEnd(int end) {
+    this.end = end;
+    return this;
+  }
+
+  public AccessLogElement getAccessLogElement() {
+    return accessLogElement;
+  }
+
+  public AccessLogElementExtraction setAccessLogElement(
+      AccessLogElement accessLogElement) {
+    this.accessLogElement = accessLogElement;
+    return this;
+  }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb = new StringBuilder("AccessLogElementExtraction{");
+    sb.append("start=").append(start);
+    sb.append(", end=").append(end);
+    sb.append(", accessLogElement=").append(accessLogElement);
+    sb.append('}');
+    return sb.toString();
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogPatternParser.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogPatternParser.java
new file mode 100644
index 0000000..cb31792
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/AccessLogPatternParser.java
@@ -0,0 +1,7 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser;
+
+import java.util.List;
+
+public interface AccessLogPatternParser {
+  List<AccessLogElementExtraction> parsePattern(String rawPattern);
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/impl/DefaultAccessLogPatternParser.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/impl/DefaultAccessLogPatternParser.java
new file mode 100644
index 0000000..a57dcb4
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/impl/DefaultAccessLogPatternParser.java
@@ -0,0 +1,112 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.PlainTextElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogPatternParser;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.AccessLogElementMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.BytesWrittenV1Matcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.BytesWrittenV2Matcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.CookieElementMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.DatetimeConfigurableMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.DatetimeMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.DurationMillisecondMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.DurationSecondMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.FirstLineOfRequestMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.LocalHostMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.LocalPortMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.MethodMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.QueryOnlyMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.RemoteHostMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.RequestHeaderElementMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.ResponseHeaderElementMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.StatusMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.UriPathIncludeQueryMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.UriPathOnlyMatcher;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl.VersionOrProtocolMatcher;
+
+public class DefaultAccessLogPatternParser implements AccessLogPatternParser {
+  private static final List<AccessLogElementMatcher> MATCHER_LIST = Arrays.asList(
+      new RequestHeaderElementMatcher(),
+      new DatetimeConfigurableMatcher(),
+      new CookieElementMatcher(),
+      new ResponseHeaderElementMatcher(),
+      new DurationSecondMatcher(),
+      new VersionOrProtocolMatcher(),
+      new BytesWrittenV1Matcher(),
+      new BytesWrittenV2Matcher(),
+      new DurationMillisecondMatcher(),
+      new LocalPortMatcher(),
+      new LocalHostMatcher(),
+      new UriPathIncludeQueryMatcher(),
+      new FirstLineOfRequestMatcher(),
+      new DatetimeMatcher(),
+      new RemoteHostMatcher(),
+      new MethodMatcher(),
+      new QueryOnlyMatcher(),
+      new UriPathOnlyMatcher(),
+      new StatusMatcher()
+  );
+
+  public static final Comparator<AccessLogElementExtraction> ACCESS_LOG_ELEMENT_EXTRACTION_COMPARATOR = (e1, e2) -> {
+    return e1.getStart() - e2.getStart();
+  };
+
+  @Override
+  public List<AccessLogElementExtraction> parsePattern(String rawPattern) {
+    List<AccessLogElementExtraction> extractionList = new ArrayList<>();
+    for (AccessLogElementMatcher matcher : MATCHER_LIST) {
+      List<AccessLogElementExtraction> extractions = matcher.extractElementPlaceholder(rawPattern);
+      if (null != extractions) {
+        extractionList.addAll(extractions);
+      }
+    }
+
+    Collections.sort(extractionList, ACCESS_LOG_ELEMENT_EXTRACTION_COMPARATOR);
+    checkExtractionList(extractionList);
+    fillInPlainTextElement(rawPattern, extractionList);
+
+    return extractionList;
+  }
+
+  private void checkExtractionList(List<AccessLogElementExtraction> extractionList) {
+    int preEnd = -1;
+    for (AccessLogElementExtraction extraction : extractionList) {
+      if (preEnd > extraction.getStart()) {
+        throw new IllegalArgumentException("access log pattern contains illegal placeholder, please check it.");
+      }
+
+      preEnd = extraction.getEnd();
+    }
+  }
+
+  /**
+   * The content not matched in rawPattern will be printed as it is, so should be converted to {@link PlainTextElement}
+   * @param rawPattern
+   * @param extractionList
+   */
+  private void fillInPlainTextElement(String rawPattern, List<AccessLogElementExtraction> extractionList) {
+    int cursor = 0;
+    List<AccessLogElementExtraction> plainTextExtractionList = new ArrayList<>();
+    for (AccessLogElementExtraction extraction : extractionList) {
+      if (cursor < extraction.getStart()) {
+        plainTextExtractionList.add(
+            new AccessLogElementExtraction()
+                .setStart(cursor)
+                .setEnd(extraction.getStart())
+                .setAccessLogElement(
+                    new PlainTextElement(rawPattern.substring(cursor, extraction.getStart()))
+                ));
+      }
+      cursor = extraction.getEnd();
+    }
+
+    extractionList.addAll(plainTextExtractionList);
+    Collections.sort(extractionList, ACCESS_LOG_ELEMENT_EXTRACTION_COMPARATOR);
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/AccessLogElementMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/AccessLogElementMatcher.java
new file mode 100644
index 0000000..420c871
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/AccessLogElementMatcher.java
@@ -0,0 +1,18 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher;
+
+import java.util.List;
+
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+/**
+ * match placeholder in raw pattern
+ */
+public interface AccessLogElementMatcher {
+  /**
+   * extract placeholders from rawPattern that match this element.
+   *
+   * @param rawPattern
+   * @return
+   */
+  List<AccessLogElementExtraction> extractElementPlaceholder(String rawPattern);
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/BytesWrittenV1Matcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/BytesWrittenV1Matcher.java
new file mode 100644
index 0000000..5cbc0cc
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/BytesWrittenV1Matcher.java
@@ -0,0 +1,19 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.BytesWrittenV1Element;
+
+public class BytesWrittenV1Matcher extends SinglePatternImmutableElementMatcher {
+
+  public static final BytesWrittenV1Element ELEMENT = new BytesWrittenV1Element();
+
+  @Override
+  protected String getPlaceholderPattern() {
+    return "%B";
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement() {
+    return ELEMENT;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/BytesWrittenV2Matcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/BytesWrittenV2Matcher.java
new file mode 100644
index 0000000..f9d7c97
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/BytesWrittenV2Matcher.java
@@ -0,0 +1,19 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.BytesWrittenV2Element;
+
+public class BytesWrittenV2Matcher extends SinglePatternImmutableElementMatcher {
+
+  public static final BytesWrittenV2Element ELEMENT = new BytesWrittenV2Element();
+
+  @Override
+  protected String getPlaceholderPattern() {
+    return "%b";
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement() {
+    return ELEMENT;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/ConfigurableAccessLogElementMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/ConfigurableAccessLogElementMatcher.java
new file mode 100644
index 0000000..2f3b9ca
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/ConfigurableAccessLogElementMatcher.java
@@ -0,0 +1,56 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.AccessLogElementMatcher;
+
+public abstract class ConfigurableAccessLogElementMatcher implements AccessLogElementMatcher {
+
+  @Override
+  public List<AccessLogElementExtraction> extractElementPlaceholder(String rawPattern) {
+    List<AccessLogElementExtraction> extractionList = new ArrayList<>();
+    int begin = -1;
+    int end = 0;
+    int cursor = 0;
+
+    while (true) {
+      end = rawPattern.indexOf(getPlaceholderSuffix(), cursor);
+      if (end < 0) {
+        break;
+      }
+      begin = locateBeginIndex(rawPattern, end, cursor);
+      if (begin < 0) {
+        break;
+      }
+
+      String identifier = rawPattern.substring(begin + getPlaceholderSuffix().length(), end);
+      extractionList.add(new AccessLogElementExtraction(begin, end + getPlaceholderSuffix().length(),
+          getAccessLogElement(identifier)));
+
+      cursor = end + 1;
+    }
+    return extractionList;
+  }
+
+  private int locateBeginIndex(String rawPattern, int end, int cursor) {
+    int preBegin = rawPattern.indexOf(getPlaceholderPrefix(), cursor);
+    int begin = rawPattern.indexOf(getPlaceholderPrefix(), preBegin + 1);
+    while (begin >= 0 && begin < end) {
+      if (begin < end) {
+        preBegin = begin;
+      }
+      begin = rawPattern.indexOf(getPlaceholderPrefix(), preBegin + 1);
+    }
+    return preBegin;
+  }
+
+
+  protected abstract String getPlaceholderSuffix();
+
+  protected abstract String getPlaceholderPrefix();
+
+  protected abstract AccessLogElement getAccessLogElement(String identifier);
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/CookieElementMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/CookieElementMatcher.java
new file mode 100644
index 0000000..b289118
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/CookieElementMatcher.java
@@ -0,0 +1,25 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.CookieElement;
+
+public class CookieElementMatcher extends ConfigurableAccessLogElementMatcher {
+  public static final String PLACEHOLDER_PREFIX = "%{";
+
+  public static final String PLACEHOLDER_SUFFIX = "}c";
+
+  @Override
+  protected String getPlaceholderSuffix() {
+    return PLACEHOLDER_SUFFIX;
+  }
+
+  @Override
+  protected String getPlaceholderPrefix() {
+    return PLACEHOLDER_PREFIX;
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement(String identifier) {
+    return new CookieElement(identifier);
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DatetimeConfigurableMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DatetimeConfigurableMatcher.java
new file mode 100644
index 0000000..d0ab988
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DatetimeConfigurableMatcher.java
@@ -0,0 +1,34 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.DatetimeConfigurableElement;
+
+/**
+ * There are two kinds of configurable datetime placeholder:
+ * <ul>
+ *   <li>%{PATTERN}t</li>
+ *   <li>%{PATTERN|TIMEZONE|LOCALE}t</li>
+ * </ul>
+ */
+public class DatetimeConfigurableMatcher extends ConfigurableAccessLogElementMatcher {
+
+  public static final String PLACEHOLDER_PREFIX = "%{";
+
+  public static final String PLACEHOLDER_SUFFIX = "}t";
+
+
+  @Override
+  protected String getPlaceholderSuffix() {
+    return PLACEHOLDER_SUFFIX;
+  }
+
+  @Override
+  protected String getPlaceholderPrefix() {
+    return PLACEHOLDER_PREFIX;
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement(String identifier) {
+    return new DatetimeConfigurableElement(identifier);
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DatetimeMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DatetimeMatcher.java
new file mode 100644
index 0000000..8b519e4
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DatetimeMatcher.java
@@ -0,0 +1,19 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.DatetimeConfigurableElement;
+
+public class DatetimeMatcher extends SinglePatternImmutableElementMatcher {
+
+  public static final DatetimeConfigurableElement ELEMENT = new DatetimeConfigurableElement();
+
+  @Override
+  protected String getPlaceholderPattern() {
+    return "%t";
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement() {
+    return ELEMENT;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DurationMillisecondMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DurationMillisecondMatcher.java
new file mode 100644
index 0000000..3e68c41
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DurationMillisecondMatcher.java
@@ -0,0 +1,19 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.DurationMillisecondElement;
+
+public class DurationMillisecondMatcher extends SinglePatternImmutableElementMatcher {
+
+  public static final DurationMillisecondElement ELEMENT = new DurationMillisecondElement();
+
+  @Override
+  protected String getPlaceholderPattern() {
+    return "%D";
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement() {
+    return ELEMENT;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DurationSecondMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DurationSecondMatcher.java
new file mode 100644
index 0000000..cb51b4c
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DurationSecondMatcher.java
@@ -0,0 +1,19 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.DurationSecondElement;
+
+public class DurationSecondMatcher extends SinglePatternImmutableElementMatcher {
+
+  public static final DurationSecondElement ELEMENT = new DurationSecondElement();
+
+  @Override
+  protected String getPlaceholderPattern() {
+    return "%T";
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement() {
+    return ELEMENT;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/FirstLineOfRequestMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/FirstLineOfRequestMatcher.java
new file mode 100644
index 0000000..6c87997
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/FirstLineOfRequestMatcher.java
@@ -0,0 +1,19 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.FirstLineOfRequestElement;
+
+public class FirstLineOfRequestMatcher extends SinglePatternImmutableElementMatcher {
+
+  public static final FirstLineOfRequestElement ELEMENT = new FirstLineOfRequestElement();
+
+  @Override
+  protected String getPlaceholderPattern() {
+    return "%r";
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement() {
+    return ELEMENT;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/ImmutableAccessLogElementMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/ImmutableAccessLogElementMatcher.java
new file mode 100644
index 0000000..c1885df
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/ImmutableAccessLogElementMatcher.java
@@ -0,0 +1,36 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import java.util.List;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.AccessLogElementMatcher;
+
+/**
+ * There are log elements that have no configuration parameter, so these elements can be immutable (and singleton).
+ * <br/>
+ * Therefore, the matching algorithm implementation of these elements can be extracted into a universal method.
+ */
+public abstract class ImmutableAccessLogElementMatcher implements AccessLogElementMatcher {
+
+  protected void matchElementPlaceholder(String rawPattern, String pattern,
+      List<AccessLogElementExtraction> extractionList) {
+    int start = -1;
+    int cursor = 0;
+    while (true) {
+      start = rawPattern.indexOf(pattern, cursor);
+
+      if (start < 0) {
+        break;
+      } else {
+        AccessLogElementExtraction extraction = new AccessLogElementExtraction(start, start + pattern.length(),
+            getAccessLogElement());
+        extractionList.add(extraction);
+      }
+
+      cursor = start + pattern.length();
+    }
+  }
+
+  protected abstract AccessLogElement getAccessLogElement();
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/LocalHostMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/LocalHostMatcher.java
new file mode 100644
index 0000000..70da46f
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/LocalHostMatcher.java
@@ -0,0 +1,19 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.LocalHostElement;
+
+public class LocalHostMatcher extends SinglePatternImmutableElementMatcher {
+
+  public static final LocalHostElement ELEMENT = new LocalHostElement();
+
+  @Override
+  protected String getPlaceholderPattern() {
+    return "%v";
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement() {
+    return ELEMENT;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/LocalPortMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/LocalPortMatcher.java
new file mode 100644
index 0000000..43dfd09
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/LocalPortMatcher.java
@@ -0,0 +1,19 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.LocalPortElement;
+
+public class LocalPortMatcher extends SinglePatternImmutableElementMatcher {
+
+  public static final LocalPortElement ELEMENT = new LocalPortElement();
+
+  @Override
+  protected String getPlaceholderPattern() {
+    return "%p";
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement() {
+    return ELEMENT;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/MethodMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/MethodMatcher.java
new file mode 100644
index 0000000..04b5c89
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/MethodMatcher.java
@@ -0,0 +1,20 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.MethodElement;
+
+public class MethodMatcher extends MultiPatternImmutableElementMatcher {
+  public static final String[] PLACEHOLDER_PATTERNS = {"%m", "cs-method"};
+
+  public static final MethodElement ELEMENT = new MethodElement();
+
+  @Override
+  protected String[] getPlaceholderPatterns() {
+    return PLACEHOLDER_PATTERNS;
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement() {
+    return ELEMENT;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/MultiPatternImmutableElementMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/MultiPatternImmutableElementMatcher.java
new file mode 100644
index 0000000..2a17c13
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/MultiPatternImmutableElementMatcher.java
@@ -0,0 +1,22 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public abstract class MultiPatternImmutableElementMatcher extends ImmutableAccessLogElementMatcher {
+  @Override
+  public List<AccessLogElementExtraction> extractElementPlaceholder(String rawPattern) {
+    List<AccessLogElementExtraction> extractionList = new ArrayList<>();
+
+    String[] patterns = getPlaceholderPatterns();
+    for (String pattern : patterns) {
+      matchElementPlaceholder(rawPattern, pattern, extractionList);
+    }
+
+    return extractionList;
+  }
+
+  protected abstract String[] getPlaceholderPatterns();
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/QueryOnlyMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/QueryOnlyMatcher.java
new file mode 100644
index 0000000..52205dd
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/QueryOnlyMatcher.java
@@ -0,0 +1,19 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.QueryOnlyElement;
+
+public class QueryOnlyMatcher extends MultiPatternImmutableElementMatcher {
+
+  public static final String[] PLACEHOLDER_PATTERNS = {"%q", "cs-uri-query"};
+
+  @Override
+  protected String[] getPlaceholderPatterns() {
+    return PLACEHOLDER_PATTERNS;
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement() {
+    return new QueryOnlyElement();
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/RemoteHostMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/RemoteHostMatcher.java
new file mode 100644
index 0000000..81b2017
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/RemoteHostMatcher.java
@@ -0,0 +1,19 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.RemoteHostElement;
+
+public class RemoteHostMatcher extends SinglePatternImmutableElementMatcher {
+
+  public static final RemoteHostElement ELEMENT = new RemoteHostElement();
+
+  @Override
+  protected String getPlaceholderPattern() {
+    return "%h";
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement() {
+    return ELEMENT;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/RequestHeaderElementMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/RequestHeaderElementMatcher.java
new file mode 100644
index 0000000..75aff75
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/RequestHeaderElementMatcher.java
@@ -0,0 +1,25 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.RequestHeaderElement;
+
+public class RequestHeaderElementMatcher extends ConfigurableAccessLogElementMatcher {
+  public static final String PLACEHOLDER_PREFIX = "%{";
+
+  public static final String PLACEHOLDER_SUFFIX = "}i";
+
+  @Override
+  protected String getPlaceholderSuffix() {
+    return PLACEHOLDER_SUFFIX;
+  }
+
+  @Override
+  protected String getPlaceholderPrefix() {
+    return PLACEHOLDER_PREFIX;
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement(String identifier) {
+    return new RequestHeaderElement(identifier);
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/ResponseHeaderElementMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/ResponseHeaderElementMatcher.java
new file mode 100644
index 0000000..45fcf7f
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/ResponseHeaderElementMatcher.java
@@ -0,0 +1,25 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.ResponseHeaderElement;
+
+public class ResponseHeaderElementMatcher extends ConfigurableAccessLogElementMatcher {
+  public static final String PLACEHOLDER_PREFIX = "%{";
+
+  public static final String PLACEHOLDER_SUFFIX = "}o";
+
+  @Override
+  protected String getPlaceholderSuffix() {
+    return PLACEHOLDER_SUFFIX;
+  }
+
+  @Override
+  protected String getPlaceholderPrefix() {
+    return PLACEHOLDER_PREFIX;
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement(String identifier) {
+    return new ResponseHeaderElement(identifier);
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/SinglePatternImmutableElementMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/SinglePatternImmutableElementMatcher.java
new file mode 100644
index 0000000..d334198
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/SinglePatternImmutableElementMatcher.java
@@ -0,0 +1,20 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public abstract class SinglePatternImmutableElementMatcher extends ImmutableAccessLogElementMatcher {
+  @Override
+  public List<AccessLogElementExtraction> extractElementPlaceholder(String rawPattern) {
+    final String pattern = getPlaceholderPattern();
+    List<AccessLogElementExtraction> extractionList = new ArrayList<>();
+
+    matchElementPlaceholder(rawPattern, pattern, extractionList);
+
+    return extractionList;
+  }
+
+  protected abstract String getPlaceholderPattern();
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/StatusMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/StatusMatcher.java
new file mode 100644
index 0000000..4084859
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/StatusMatcher.java
@@ -0,0 +1,19 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.StatusElement;
+
+public class StatusMatcher extends MultiPatternImmutableElementMatcher {
+
+  public static final String[] PLACEHOLDER_PATTERNS = {"%s", "cs-status"};
+
+  @Override
+  protected String[] getPlaceholderPatterns() {
+    return PLACEHOLDER_PATTERNS;
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement() {
+    return new StatusElement();
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/UriPathIncludeQueryMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/UriPathIncludeQueryMatcher.java
new file mode 100644
index 0000000..8d23733
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/UriPathIncludeQueryMatcher.java
@@ -0,0 +1,55 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.UriPathIncludeQueryElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.AccessLogElementMatcher;
+
+public class UriPathIncludeQueryMatcher implements AccessLogElementMatcher {
+
+  public static final UriPathIncludeQueryElement ELEMENT = new UriPathIncludeQueryElement();
+
+  public static final String PLACEHOLDER_PATTERN = "cs-uri";
+
+  public static final String[] EXCLUDE_PATTERNS = new String[] {"cs-uri-stem", "cs-uri-query"};
+
+  @Override
+  public List<AccessLogElementExtraction> extractElementPlaceholder(String rawPattern) {
+    List<AccessLogElementExtraction> extractionList = new ArrayList<>();
+
+    int start = -1;
+    int cursor = 0;
+    while (true) {
+      start = rawPattern.indexOf(PLACEHOLDER_PATTERN, cursor);
+      if (start < 0) {
+        break;
+      }
+
+      if (shouldExclude(rawPattern, start, cursor)) {
+        cursor += PLACEHOLDER_PATTERN.length();
+        continue;
+      }
+
+      AccessLogElementExtraction extraction = new AccessLogElementExtraction(start,
+          start + PLACEHOLDER_PATTERN.length(),
+          ELEMENT);
+      extractionList.add(extraction);
+
+      cursor = start + PLACEHOLDER_PATTERN.length();
+    }
+
+    return extractionList;
+  }
+
+  private boolean shouldExclude(String rawPattern, final int start, final int cursor) {
+    for (String exclude : EXCLUDE_PATTERNS) {
+      if (start == rawPattern.indexOf(exclude, cursor)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/UriPathOnlyMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/UriPathOnlyMatcher.java
new file mode 100644
index 0000000..a644e50
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/UriPathOnlyMatcher.java
@@ -0,0 +1,19 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.UriPathOnlyElement;
+
+public class UriPathOnlyMatcher extends MultiPatternImmutableElementMatcher {
+
+  public static final String[] PLACEHOLDER_PATTERNS = {"%U", "cs-uri-stem"};
+
+  @Override
+  protected String[] getPlaceholderPatterns() {
+    return PLACEHOLDER_PATTERNS;
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement() {
+    return new UriPathOnlyElement();
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/VersionOrProtocolMatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/VersionOrProtocolMatcher.java
new file mode 100644
index 0000000..374a2d3
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/VersionOrProtocolMatcher.java
@@ -0,0 +1,19 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.VersionOrProtocolElement;
+
+public class VersionOrProtocolMatcher extends SinglePatternImmutableElementMatcher {
+
+  public static final VersionOrProtocolElement ELEMENT = new VersionOrProtocolElement();
+
+  @Override
+  protected String getPlaceholderPattern() {
+    return "%H";
+  }
+
+  @Override
+  protected AccessLogElement getAccessLogElement() {
+    return ELEMENT;
+  }
+}
diff --git a/transports/transport-rest/transport-rest-vertx/src/main/resources/config/base/log4j.properties b/transports/transport-rest/transport-rest-vertx/src/main/resources/config/base/log4j.properties
new file mode 100644
index 0000000..cbade69
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/main/resources/config/base/log4j.properties
@@ -0,0 +1,13 @@
+# access log default configuration
+paas.logs.accesslog.dir=${paas.logs.dir}
+paas.logs.accesslog.file=cse.access.log
+# access log File appender
+log4j.logger.accesslog=INFO,access
+log4j.appender.access=io.servicecomb.foundation.common.utils.RollingFileAppenderExt
+log4j.appender.access.MaxBackupIndex=10
+log4j.appender.access.MaxFileSize=20MB
+log4j.appender.access.file=${paas.logs.accesslog.dir}${paas.logs.accesslog.file}
+log4j.appender.access.layout=org.apache.log4j.PatternLayout
+log4j.appender.access.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss,SSS/zzz}] %m%n
+log4j.appender.access.logPermission=rw-------
+log4j.additivity.accesslog=false
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/BytesWrittenV1ElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/BytesWrittenV1ElementTest.java
new file mode 100644
index 0000000..ecf66f7
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/BytesWrittenV1ElementTest.java
@@ -0,0 +1,63 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.ext.web.RoutingContext;
+
+public class BytesWrittenV1ElementTest {
+
+  private static final BytesWrittenV1Element ELEMENT = new BytesWrittenV1Element();
+
+  @Test
+  public void getFormattedElement() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+    HttpServerResponse mockResponse = Mockito.mock(HttpServerResponse.class);
+    long bytesWritten = 16l;
+
+    param.setRoutingContext(mockContext);
+    Mockito.when(mockContext.response()).thenReturn(mockResponse);
+    Mockito.when(mockResponse.bytesWritten()).thenReturn(bytesWritten);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals(String.valueOf(bytesWritten), result);
+  }
+
+
+  @Test
+  public void getFormattedElementOnResponseIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+
+    param.setRoutingContext(mockContext);
+    Mockito.when(mockContext.response()).thenReturn(null);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("0", result);
+  }
+
+
+  @Test
+  public void getFormattedElementOnBytesWrittenIsZero() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+    HttpServerResponse mockResponse = Mockito.mock(HttpServerResponse.class);
+    long bytesWritten = 0l;
+
+    param.setRoutingContext(mockContext);
+    Mockito.when(mockContext.response()).thenReturn(mockResponse);
+    Mockito.when(mockResponse.bytesWritten()).thenReturn(bytesWritten);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("0", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/BytesWrittenV2ElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/BytesWrittenV2ElementTest.java
new file mode 100644
index 0000000..6eb19f1
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/BytesWrittenV2ElementTest.java
@@ -0,0 +1,61 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.ext.web.RoutingContext;
+
+public class BytesWrittenV2ElementTest {
+
+  public static final BytesWrittenV2Element ELEMENT = new BytesWrittenV2Element();
+
+  @Test
+  public void getFormattedElement() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+    HttpServerResponse mockResponse = Mockito.mock(HttpServerResponse.class);
+    long bytesWritten = 16L;
+
+    param.setRoutingContext(mockContext);
+    Mockito.when(mockContext.response()).thenReturn(mockResponse);
+    Mockito.when(mockResponse.bytesWritten()).thenReturn(bytesWritten);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals(String.valueOf(bytesWritten), result);
+  }
+
+
+  @Test
+  public void getFormattedElementOnResponseIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+
+    param.setRoutingContext(mockContext);
+    Mockito.when(mockContext.response()).thenReturn(null);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+
+  @Test
+  public void getFormattedElementOnBytesWrittenIsZero() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+    HttpServerResponse mockResponse = Mockito.mock(HttpServerResponse.class);
+    long bytesWritten = 0l;
+
+    param.setRoutingContext(mockContext);
+    Mockito.when(mockContext.response()).thenReturn(mockResponse);
+    Mockito.when(mockResponse.bytesWritten()).thenReturn(bytesWritten);
+
+    String result = ELEMENT.getFormattedElement(param);
+    assertEquals("-", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/CookieElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/CookieElementTest.java
new file mode 100644
index 0000000..65d135b
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/CookieElementTest.java
@@ -0,0 +1,86 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.*;
+
+import java.util.HashSet;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.vertx.ext.web.Cookie;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.impl.CookieImpl;
+
+public class CookieElementTest {
+
+  public static final String COOKIE_NAME = "cookieName";
+
+  private static final CookieElement ELEMENT = new CookieElement(COOKIE_NAME);
+
+  @Test
+  public void getFormattedElement() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+    HashSet<Cookie> cookieSet = new HashSet<>();
+    String cookieValue = "cookieValue";
+    CookieImpl cookie = new CookieImpl(COOKIE_NAME, cookieValue);
+
+    cookieSet.add(cookie);
+    Mockito.when(mockContext.cookieCount()).thenReturn(1);
+    Mockito.when(mockContext.cookies()).thenReturn(cookieSet);
+    param.setRoutingContext(mockContext);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    Assert.assertEquals(cookieValue, result);
+  }
+
+  @Test
+  public void getFormattedElementOnCookieCountIsZero() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+    HashSet<Cookie> cookieSet = new HashSet<>();
+
+    Mockito.when(mockContext.cookieCount()).thenReturn(0);
+    Mockito.when(mockContext.cookies()).thenReturn(cookieSet);
+    param.setRoutingContext(mockContext);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    Assert.assertEquals("-", result);
+  }
+
+  @Test
+  public void getFormattedElementOnCookieSetIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+
+    Mockito.when(mockContext.cookieCount()).thenReturn(1);
+    Mockito.when(mockContext.cookies()).thenReturn(null);
+    param.setRoutingContext(mockContext);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    Assert.assertEquals("-", result);
+  }
+
+  @Test
+  public void getFormattedElementOnNotFound() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+    HashSet<Cookie> cookieSet = new HashSet<>();
+    String cookieValue = "cookieValue";
+    CookieImpl cookie = new CookieImpl("anotherCookieName", cookieValue);
+
+    cookieSet.add(cookie);
+    Mockito.when(mockContext.cookieCount()).thenReturn(1);
+    Mockito.when(mockContext.cookies()).thenReturn(cookieSet);
+    param.setRoutingContext(mockContext);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    Assert.assertEquals("-", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DatetimeConfigurableElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DatetimeConfigurableElementTest.java
new file mode 100644
index 0000000..63711f1
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DatetimeConfigurableElementTest.java
@@ -0,0 +1,101 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+
+public class DatetimeConfigurableElementTest {
+
+  @Test
+  public void getFormattedElement() {
+    DatetimeConfigurableElement element = new DatetimeConfigurableElement(
+        "EEE, yyyy MMM dd HH:mm:ss zzz|GMT-08|zh-CN");
+
+    AccessLogParam accessLogParam = new AccessLogParam().setStartMillisecond(1416863450581L);
+
+    String result = element.getFormattedElement(accessLogParam);
+
+    assertEquals("星期一, 2014 十一月 24 13:10:50 GMT-08:00", result);
+  }
+
+  @Test
+  public void getFormattedElementOnNoPattern() {
+    DatetimeConfigurableElement element = new DatetimeConfigurableElement(
+        "|GMT+08|zh-CN");
+
+    AccessLogParam accessLogParam = new AccessLogParam().setStartMillisecond(1416863450581L);
+
+    String result = element.getFormattedElement(accessLogParam);
+
+    assertEquals("星期二, 25 十一月 2014 05:10:50 GMT+08:00", result);
+  }
+
+  @Test
+  public void getFormattedElementOnNoTimezone() {
+    DatetimeConfigurableElement element = new DatetimeConfigurableElement(
+        "yyyy/MM/dd zzz||zh-CN");
+
+    AccessLogParam accessLogParam = new AccessLogParam().setStartMillisecond(1416863450581L);
+
+    String result = element.getFormattedElement(accessLogParam);
+
+    assertEquals("2014/11/25 CST", result);
+  }
+
+  @Test
+  public void getFormattedElementOnNoLocale() {
+    DatetimeConfigurableElement element = new DatetimeConfigurableElement(
+        "EEE, dd MMM yyyy HH:mm:ss zzz|GMT+08|");
+
+    AccessLogParam accessLogParam = new AccessLogParam().setStartMillisecond(1416863450581L);
+
+    String result = element.getFormattedElement(accessLogParam);
+
+    assertEquals("Tue, 25 Nov 2014 05:10:50 GMT+08:00", result);
+  }
+
+  @Test
+  public void getFormattedElementOnNoConfig() {
+    DatetimeConfigurableElement element = new DatetimeConfigurableElement(
+        "||");
+
+    AccessLogParam accessLogParam = new AccessLogParam().setStartMillisecond(1416863450581L);
+
+    String result = element.getFormattedElement(accessLogParam);
+
+    assertEquals("Tue, 25 Nov 2014 05:10:50 CST", result);
+  }
+
+  @Test
+  public void testConstructorWithNoArg() {
+    DatetimeConfigurableElement element = new DatetimeConfigurableElement();
+
+    AccessLogParam accessLogParam = new AccessLogParam().setStartMillisecond(1416863450581L);
+
+    String result = element.getFormattedElement(accessLogParam);
+
+    assertEquals("EEE, dd MMM yyyy HH:mm:ss zzz", element.getPattern());
+    assertEquals(Locale.US, element.getLocale());
+    assertEquals(TimeZone.getDefault(), element.getTimezone());
+    assertEquals("Tue, 25 Nov 2014 05:10:50 CST", result);
+  }
+
+  @Test
+  public void testConstructorWithNoSeparator() {
+    DatetimeConfigurableElement element = new DatetimeConfigurableElement("yyyy/MM/dd HH:mm:ss zzz");
+
+    AccessLogParam accessLogParam = new AccessLogParam().setStartMillisecond(1416863450581L);
+
+    String result = element.getFormattedElement(accessLogParam);
+
+    assertEquals("yyyy/MM/dd HH:mm:ss zzz", element.getPattern());
+    assertEquals(Locale.US, element.getLocale());
+    assertEquals(TimeZone.getDefault(), element.getTimezone());
+    assertEquals("2014/11/25 05:10:50 CST", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DurationMillisecondElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DurationMillisecondElementTest.java
new file mode 100644
index 0000000..a8aa0a2
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DurationMillisecondElementTest.java
@@ -0,0 +1,21 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+
+public class DurationMillisecondElementTest {
+
+  public static final DurationMillisecondElement ELEMENT = new DurationMillisecondElement();
+
+  @Test
+  public void getFormattedElement() {
+    AccessLogParam param = new AccessLogParam().setStartMillisecond(1L).setEndMillisecond(2L);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("1", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DurationSecondElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DurationSecondElementTest.java
new file mode 100644
index 0000000..8c15794
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/DurationSecondElementTest.java
@@ -0,0 +1,41 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+
+public class DurationSecondElementTest {
+
+  public static final DurationSecondElement ELEMENT = new DurationSecondElement();
+
+  @Test
+  public void getFormattedElementOn999ms() {
+    AccessLogParam param = new AccessLogParam().setStartMillisecond(1L).setEndMillisecond(1000L);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("0", result);
+  }
+
+  @Test
+  public void getFormattedElementOn1000ms() {
+    AccessLogParam param = new AccessLogParam().setStartMillisecond(1L).setEndMillisecond(1001L);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("1", result);
+  }
+
+  @Test
+  public void getFormattedElementOn1001ms() {
+    AccessLogParam param = new AccessLogParam().setStartMillisecond(1L).setEndMillisecond(1002L);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("1", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/FirstLineOfRequestElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/FirstLineOfRequestElementTest.java
new file mode 100644
index 0000000..3626b24
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/FirstLineOfRequestElementTest.java
@@ -0,0 +1,36 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.vertx.core.http.HttpMethod;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.http.HttpVersion;
+import io.vertx.ext.web.RoutingContext;
+
+public class FirstLineOfRequestElementTest {
+
+  public static final FirstLineOfRequestElement ELEMENT = new FirstLineOfRequestElement();
+
+  @Test
+  public void getFormattedElement() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    String uri = "/test/uri";
+
+    param.setRoutingContext(mockContext);
+    Mockito.when(mockContext.request()).thenReturn(request);
+    Mockito.when(request.method()).thenReturn(HttpMethod.DELETE);
+    Mockito.when(request.path()).thenReturn(uri);
+    Mockito.when(request.version()).thenReturn(HttpVersion.HTTP_1_1);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("DELETE \"" + uri + "\" HTTP/1.1", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/LocalHostElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/LocalHostElementTest.java
new file mode 100644
index 0000000..4d64116
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/LocalHostElementTest.java
@@ -0,0 +1,100 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.net.SocketAddress;
+import io.vertx.ext.web.RoutingContext;
+
+public class LocalHostElementTest {
+
+  public static final LocalHostElement ELEMENT = new LocalHostElement();
+
+  @Test
+  public void getFormattedElement() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    SocketAddress localAddress = Mockito.mock(SocketAddress.class);
+    String localHost = "testHost";
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.localAddress()).thenReturn(localAddress);
+    Mockito.when(localAddress.host()).thenReturn(localHost);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals(localHost, result);
+  }
+
+  @Test
+  public void getFormattedElementOnRequestIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(null);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+  @Test
+  public void getFormattedElementOnLocalAddressIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.localAddress()).thenReturn(null);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+  @Test
+  public void getFormattedElementOnHostIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    SocketAddress localAddress = Mockito.mock(SocketAddress.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.localAddress()).thenReturn(localAddress);
+    Mockito.when(localAddress.host()).thenReturn(null);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+  @Test
+  public void getFormattedElementIsEmpty() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    SocketAddress localAddress = Mockito.mock(SocketAddress.class);
+    String localHost = "";
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.localAddress()).thenReturn(localAddress);
+    Mockito.when(localAddress.host()).thenReturn(localHost);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/LocalPortElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/LocalPortElementTest.java
new file mode 100644
index 0000000..6d33e0c
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/LocalPortElementTest.java
@@ -0,0 +1,60 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.net.SocketAddress;
+import io.vertx.ext.web.RoutingContext;
+
+public class LocalPortElementTest {
+
+  @Test
+  public void getFormattedElement() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    SocketAddress localAddress = Mockito.mock(SocketAddress.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.localAddress()).thenReturn(localAddress);
+    Mockito.when(localAddress.port()).thenReturn(8080);
+
+    String result = new LocalPortElement().getFormattedElement(param);
+
+    assertEquals("8080", result);
+  }
+
+  @Test
+  public void getFormattedElementOnRequestIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(null);
+
+    String result = new LocalPortElement().getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+  @Test
+  public void getFormattedElementOnLocalAddressIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.localAddress()).thenReturn(null);
+
+    String result = new LocalPortElement().getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/MethodElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/MethodElementTest.java
new file mode 100644
index 0000000..1a05390
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/MethodElementTest.java
@@ -0,0 +1,46 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.vertx.core.http.HttpMethod;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.ext.web.RoutingContext;
+
+public class MethodElementTest {
+
+  @Test
+  public void getFormattedElement() {
+    RoutingContext routingContext = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    Mockito.when(routingContext.request()).thenReturn(request);
+    Mockito.when(request.method()).thenReturn(HttpMethod.DELETE);
+    AccessLogParam param = new AccessLogParam().setRoutingContext(routingContext);
+
+    Assert.assertEquals("DELETE", new MethodElement().getFormattedElement(param));
+  }
+
+  @Test
+  public void getFormattedElementOnRequestIsNull() {
+    RoutingContext routingContext = Mockito.mock(RoutingContext.class);
+    AccessLogParam param = new AccessLogParam().setRoutingContext(routingContext);
+
+    Mockito.when(routingContext.request()).thenReturn(null);
+
+    Assert.assertEquals("-", new MethodElement().getFormattedElement(param));
+  }
+
+  @Test
+  public void getFormattedElementOnMethodIsNull() {
+    RoutingContext routingContext = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    AccessLogParam param = new AccessLogParam().setRoutingContext(routingContext);
+
+    Mockito.when(routingContext.request()).thenReturn(request);
+    Mockito.when(request.method()).thenReturn(null);
+
+    Assert.assertEquals("-", new MethodElement().getFormattedElement(param));
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/PlainTextElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/PlainTextElementTest.java
new file mode 100644
index 0000000..83c6cc2
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/PlainTextElementTest.java
@@ -0,0 +1,14 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class PlainTextElementTest {
+
+  @Test
+  public void getFormattedElement() {
+    PlainTextElement element = new PlainTextElement("contentTest");
+    assertEquals("contentTest", element.getFormattedElement(null));
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/QueryOnlyElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/QueryOnlyElementTest.java
new file mode 100644
index 0000000..2bc1963
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/QueryOnlyElementTest.java
@@ -0,0 +1,73 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.ext.web.RoutingContext;
+
+public class QueryOnlyElementTest {
+
+  @Test
+  public void getFormattedElement() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    String query = "?status=up";
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.query()).thenReturn(query);
+
+    String result = new QueryOnlyElement().getFormattedElement(param);
+
+    assertEquals(query, result);
+  }
+
+  @Test
+  public void getFormattedElementOnRequestIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(null);
+
+    String result = new QueryOnlyElement().getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+  @Test
+  public void getFormattedElementOnQueryIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.query()).thenReturn(null);
+
+    String result = new QueryOnlyElement().getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+  @Test
+  public void getFormattedElementOnQueryIsEmpty() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    String query = "";
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.query()).thenReturn(query);
+
+    String result = new QueryOnlyElement().getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/RemoteHostElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/RemoteHostElementTest.java
new file mode 100644
index 0000000..95d72d7
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/RemoteHostElementTest.java
@@ -0,0 +1,101 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.net.SocketAddress;
+import io.vertx.ext.web.RoutingContext;
+
+public class RemoteHostElementTest {
+
+  public static final RemoteHostElement ELEMENT = new RemoteHostElement();
+
+  @Test
+  public void getFormattedElement() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    SocketAddress address = Mockito.mock(SocketAddress.class);
+    String remoteHost = "remoteHost";
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.remoteAddress()).thenReturn(address);
+    Mockito.when(address.host()).thenReturn(remoteHost);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals(remoteHost, result);
+  }
+
+  @Test
+  public void getFormattedElementOnRequestIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(null);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+
+  @Test
+  public void getFormattedElementOnRemoteAddressIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.remoteAddress()).thenReturn(null);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+
+  @Test
+  public void getFormattedElementOnHostIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    SocketAddress address = Mockito.mock(SocketAddress.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.remoteAddress()).thenReturn(address);
+    Mockito.when(address.host()).thenReturn(null);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+
+  @Test
+  public void getFormattedElementOnHostIsEmpty() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    SocketAddress address = Mockito.mock(SocketAddress.class);
+    String remoteHost = "";
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.remoteAddress()).thenReturn(address);
+    Mockito.when(address.host()).thenReturn(remoteHost);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/RequestHeaderElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/RequestHeaderElementTest.java
new file mode 100644
index 0000000..d87c731
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/RequestHeaderElementTest.java
@@ -0,0 +1,66 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.http.impl.headers.VertxHttpHeaders;
+import io.vertx.ext.web.RoutingContext;
+
+public class RequestHeaderElementTest {
+
+  private static final String HEADER_IDENTIFIER = "headerIdentifier";
+
+  private static final RequestHeaderElement ELEMENT = new RequestHeaderElement(HEADER_IDENTIFIER);
+
+  @Test
+  public void getFormattedElement() {
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+    AccessLogParam param = new AccessLogParam().setRoutingContext(mockContext);
+    HttpServerRequest mockRequest = Mockito.mock(HttpServerRequest.class);
+    VertxHttpHeaders headers = new VertxHttpHeaders();
+    String testValue = "testValue";
+    headers.add(HEADER_IDENTIFIER, testValue);
+
+    Mockito.when(mockContext.request()).thenReturn(mockRequest);
+    Mockito.when(mockRequest.headers()).thenReturn(headers);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals(testValue, result);
+  }
+
+  @Test
+  public void getFormattedElementIfHeaderIsNull() {
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+    AccessLogParam param = new AccessLogParam().setRoutingContext(mockContext);
+    HttpServerRequest mockRequest = Mockito.mock(HttpServerRequest.class);
+
+    Mockito.when(mockContext.request()).thenReturn(mockRequest);
+    Mockito.when(mockRequest.headers()).thenReturn(null);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+  @Test
+  public void getFormattedElementIfNotFound() {
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+    AccessLogParam param = new AccessLogParam().setRoutingContext(mockContext);
+    HttpServerRequest mockRequest = Mockito.mock(HttpServerRequest.class);
+    VertxHttpHeaders headers = new VertxHttpHeaders();
+    String testValue = "testValue";
+    headers.add("anotherHeader", testValue);
+
+    Mockito.when(mockContext.request()).thenReturn(mockRequest);
+    Mockito.when(mockRequest.headers()).thenReturn(headers);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/ResponseHeaderElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/ResponseHeaderElementTest.java
new file mode 100644
index 0000000..8f71a76
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/ResponseHeaderElementTest.java
@@ -0,0 +1,85 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.core.http.impl.headers.VertxHttpHeaders;
+import io.vertx.ext.web.RoutingContext;
+
+public class ResponseHeaderElementTest {
+
+  public static final String IDENTIFIER = "identifier";
+
+  private static final ResponseHeaderElement ELEMENT = new ResponseHeaderElement(IDENTIFIER);
+
+  @Test
+  public void getFormattedElement() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+    HttpServerResponse mockResponse = Mockito.mock(HttpServerResponse.class);
+    VertxHttpHeaders headers = new VertxHttpHeaders();
+    String headerValue = "headerValue";
+
+    param.setRoutingContext(mockContext);
+    headers.add(IDENTIFIER, headerValue);
+
+    Mockito.when(mockContext.response()).thenReturn(mockResponse);
+    Mockito.when(mockResponse.headers()).thenReturn(headers);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals(headerValue, result);
+  }
+
+  @Test
+  public void getFormattedElementOnHeadersIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+    HttpServerResponse mockResponse = Mockito.mock(HttpServerResponse.class);
+
+    param.setRoutingContext(mockContext);
+
+    Mockito.when(mockContext.response()).thenReturn(mockResponse);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+  @Test
+  public void getFormattedElementOnResponseIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+
+    param.setRoutingContext(mockContext);
+
+    Mockito.when(mockContext.response()).thenReturn(null);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+  @Test
+  public void getFormattedElementOnNotFound() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext mockContext = Mockito.mock(RoutingContext.class);
+    HttpServerResponse mockResponse = Mockito.mock(HttpServerResponse.class);
+    VertxHttpHeaders headers = new VertxHttpHeaders();
+    String headerValue = "headerValue";
+
+    param.setRoutingContext(mockContext);
+    headers.add("anotherHeader", headerValue);
+
+    Mockito.when(mockContext.response()).thenReturn(mockResponse);
+    Mockito.when(mockResponse.headers()).thenReturn(headers);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/StatusElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/StatusElementTest.java
new file mode 100644
index 0000000..7ceba01
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/StatusElementTest.java
@@ -0,0 +1,45 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.ext.web.RoutingContext;
+
+public class StatusElementTest {
+
+  public static final StatusElement STATUS_ELEMENT = new StatusElement();
+
+  @Test
+  public void getFormattedElement() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerResponse response = Mockito.mock(HttpServerResponse.class);
+    int statusCode = 200;
+
+    param.setRoutingContext(context);
+    Mockito.when(context.response()).thenReturn(response);
+    Mockito.when(response.getStatusCode()).thenReturn(statusCode);
+
+    String result = STATUS_ELEMENT.getFormattedElement(param);
+
+    assertEquals("200", result);
+  }
+
+
+  @Test
+  public void getFormattedElementOnResponseIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.response()).thenReturn(null);
+
+    String result = STATUS_ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/UriPathIncludeQueryElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/UriPathIncludeQueryElementTest.java
new file mode 100644
index 0000000..b4ca646
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/UriPathIncludeQueryElementTest.java
@@ -0,0 +1,78 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.ext.web.RoutingContext;
+
+public class UriPathIncludeQueryElementTest {
+
+  public static final UriPathIncludeQueryElement ELEMENT = new UriPathIncludeQueryElement();
+
+  @Test
+  public void getFormattedElement() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    String uri = "uriTest";
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.uri()).thenReturn(uri);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals(uri, result);
+  }
+
+
+  @Test
+  public void getFormattedElementOnRequestIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(null);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+  @Test
+  public void getFormattedElementOnUriIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.uri()).thenReturn(null);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+
+  @Test
+  public void getFormattedElementOnUriIsEmpty() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    String uri = "";
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.uri()).thenReturn(uri);
+
+    String result = ELEMENT.getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/UriPathOnlyElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/UriPathOnlyElementTest.java
new file mode 100644
index 0000000..829bd0a
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/UriPathOnlyElementTest.java
@@ -0,0 +1,58 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.*;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.ext.web.RoutingContext;
+
+public class UriPathOnlyElementTest {
+
+  @Test
+  public void getFormattedElement() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    String uri = "/uri/test";
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.path()).thenReturn(uri);
+
+    String result = new UriPathOnlyElement().getFormattedElement(param);
+
+    Assert.assertEquals(uri, result);
+  }
+
+  @Test
+  public void getFormattedElementOnRequestIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(null);
+
+    String result = new UriPathOnlyElement().getFormattedElement(param);
+
+    Assert.assertEquals("-", result);
+  }
+
+  @Test
+  public void getFormattedElementOnMethodIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.path()).thenReturn(null);
+
+    String result = new UriPathOnlyElement().getFormattedElement(param);
+
+    Assert.assertEquals("-", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/VersionOrProtocolElementTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/VersionOrProtocolElementTest.java
new file mode 100644
index 0000000..c95130d
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/element/impl/VersionOrProtocolElementTest.java
@@ -0,0 +1,58 @@
+package io.servicecomb.transport.rest.vertx.accesslog.element.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.http.HttpVersion;
+import io.vertx.ext.web.RoutingContext;
+
+public class VersionOrProtocolElementTest {
+
+  @Test
+  public void getFormattedElement() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.version()).thenReturn(HttpVersion.HTTP_1_1);
+
+    String result = new VersionOrProtocolElement().getFormattedElement(param);
+
+    assertEquals("HTTP/1.1", result);
+  }
+
+  @Test
+  public void getFormattedElementOnRequestIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(null);
+
+    String result = new VersionOrProtocolElement().getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+
+
+  @Test
+  public void getFormattedElementOnVersionIsNull() {
+    AccessLogParam param = new AccessLogParam();
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+
+    param.setRoutingContext(context);
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.version()).thenReturn(null);
+
+    String result = new VersionOrProtocolElement().getFormattedElement(param);
+
+    assertEquals("-", result);
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/impl/AccessLogHandlerImplTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/impl/AccessLogHandlerImplTest.java
new file mode 100644
index 0000000..19a0bbc
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/impl/AccessLogHandlerImplTest.java
@@ -0,0 +1,72 @@
+package io.servicecomb.transport.rest.vertx.accesslog.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+
+import io.servicecomb.transport.rest.vertx.accesslog.AccessLogParam;
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.DatetimeConfigurableElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.MethodElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.PlainTextElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+import io.vertx.core.http.HttpMethod;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.ext.web.RoutingContext;
+import mockit.Deencapsulation;
+
+public class AccessLogHandlerImplTest {
+
+  public static final AccessLogElement methodElement = new MethodElement();
+
+  public static final AccessLogElement datetimeElement = new DatetimeConfigurableElement();
+
+  public static final AccessLogElement plainTextElement = new PlainTextElement(" - ");
+
+  public static final Logger logger = Mockito.mock(Logger.class);
+
+  private static final AccessLogHandlerImpl accessLogHandlerImpl = new AccessLogHandlerImpl("", s -> {
+    return Arrays.asList(new AccessLogElementExtraction().setAccessLogElement(methodElement),
+        new AccessLogElementExtraction().setAccessLogElement(plainTextElement),
+        new AccessLogElementExtraction().setAccessLogElement(datetimeElement));
+  });
+
+  @BeforeClass
+  public static void init() {
+    Deencapsulation.setField(AccessLogHandlerImpl.class, "LOGGER", logger);
+  }
+
+  @Test
+  public void testConstructor() {
+    AccessLogElement[] elements = Deencapsulation.getField(accessLogHandlerImpl, "accessLogElements");
+    assertEquals(3, elements.length);
+    assertEquals(methodElement, elements[0]);
+    assertEquals(plainTextElement, elements[1]);
+    assertEquals(datetimeElement, elements[2]);
+  }
+
+  @Test
+  public void handle() {
+    RoutingContext testContext = Mockito.mock(RoutingContext.class);
+    accessLogHandlerImpl.handle(testContext);
+  }
+
+  @Test
+  public void testLog() {
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    AccessLogParam accessLogParam = new AccessLogParam().setStartMillisecond(1416863450581L).setRoutingContext(context);
+
+    Mockito.when(context.request()).thenReturn(request);
+    Mockito.when(request.method()).thenReturn(HttpMethod.DELETE);
+
+    Deencapsulation.invoke(accessLogHandlerImpl, "log", accessLogParam);
+
+    Mockito.verify(logger).info("DELETE" + " - " + "Tue, 25 Nov 2014 05:10:50 CST");
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/impl/DefaultAccessLogPatternParserTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/impl/DefaultAccessLogPatternParserTest.java
new file mode 100644
index 0000000..82992a5
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/impl/DefaultAccessLogPatternParserTest.java
@@ -0,0 +1,90 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.BytesWrittenV1Element;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.BytesWrittenV2Element;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.CookieElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.DatetimeConfigurableElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.DurationMillisecondElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.DurationSecondElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.FirstLineOfRequestElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.LocalHostElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.LocalPortElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.MethodElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.PlainTextElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.QueryOnlyElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.RemoteHostElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.RequestHeaderElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.ResponseHeaderElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.StatusElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.UriPathIncludeQueryElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.UriPathOnlyElement;
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.VersionOrProtocolElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+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";
+
+  private static DefaultAccessLogPatternParser accessLogPatternParser = new DefaultAccessLogPatternParser();
+
+  @Test
+  public void testParsePattern() {
+    List<AccessLogElementExtraction> result = accessLogPatternParser.parsePattern(ROW_PATTERN);
+    assertEquals(26, result.size());
+    assertEquals(PlainTextElement.class, result.get(0).getAccessLogElement().getClass());
+    assertEquals(MethodElement.class, result.get(1).getAccessLogElement().getClass());
+    assertEquals(PlainTextElement.class, result.get(2).getAccessLogElement().getClass());
+    assertEquals(MethodElement.class, result.get(3).getAccessLogElement().getClass());
+    assertEquals(PlainTextElement.class, result.get(4).getAccessLogElement().getClass());
+    assertEquals(StatusElement.class, result.get(5).getAccessLogElement().getClass());
+    assertEquals(DurationSecondElement.class, result.get(6).getAccessLogElement().getClass());
+    assertEquals(DurationMillisecondElement.class, result.get(7).getAccessLogElement().getClass());
+    assertEquals(RemoteHostElement.class, result.get(8).getAccessLogElement().getClass());
+    assertEquals(LocalHostElement.class, result.get(9).getAccessLogElement().getClass());
+    assertEquals(LocalPortElement.class, result.get(10).getAccessLogElement().getClass());
+    assertEquals(BytesWrittenV1Element.class, result.get(11).getAccessLogElement().getClass());
+    assertEquals(BytesWrittenV2Element.class, result.get(12).getAccessLogElement().getClass());
+    assertEquals(FirstLineOfRequestElement.class, result.get(13).getAccessLogElement().getClass());
+    assertEquals(UriPathOnlyElement.class, result.get(14).getAccessLogElement().getClass());
+    assertEquals(QueryOnlyElement.class, result.get(15).getAccessLogElement().getClass());
+    assertEquals(UriPathOnlyElement.class, result.get(16).getAccessLogElement().getClass());
+    assertEquals(QueryOnlyElement.class, result.get(17).getAccessLogElement().getClass());
+    assertEquals(UriPathIncludeQueryElement.class, result.get(18).getAccessLogElement().getClass());
+    assertEquals(VersionOrProtocolElement.class, result.get(19).getAccessLogElement().getClass());
+    assertEquals(DatetimeConfigurableElement.class, result.get(20).getAccessLogElement().getClass());
+    assertEquals(DatetimeConfigurableElement.class, result.get(21).getAccessLogElement().getClass());
+    assertEquals(DatetimeConfigurableElement.class, result.get(22).getAccessLogElement().getClass());
+    assertEquals(RequestHeaderElement.class, result.get(23).getAccessLogElement().getClass());
+    assertEquals(ResponseHeaderElement.class, result.get(24).getAccessLogElement().getClass());
+    assertEquals(CookieElement.class, result.get(25).getAccessLogElement().getClass());
+  }
+
+  @Test
+  public void testCheckExtractionList() {
+    List<AccessLogElementExtraction> extractionList = new ArrayList<>(3);
+    extractionList.add(new AccessLogElementExtraction().setStart(0).setEnd(3));
+    extractionList.add(new AccessLogElementExtraction().setStart(3).setEnd(6));
+    extractionList.add(new AccessLogElementExtraction().setStart(5).setEnd(9));
+
+    try {
+      Deencapsulation.invoke(new DefaultAccessLogPatternParser(), "checkExtractionList", extractionList);
+      fail("expect an exception");
+    } catch (Exception e) {
+      assertEquals(IllegalArgumentException.class, e.getClass());
+      assertEquals("access log pattern contains illegal placeholder, please check it.", e.getMessage());
+    }
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/BytesWrittenV1MatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/BytesWrittenV1MatcherTest.java
new file mode 100644
index 0000000..ca85678
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/BytesWrittenV1MatcherTest.java
@@ -0,0 +1,46 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.BytesWrittenV1Element;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public class BytesWrittenV1MatcherTest {
+  private static final BytesWrittenV1Matcher MATCHER = new BytesWrittenV1Matcher();
+
+  private static final String RAW_PATTERN = "%B %h %{PATTERN}t %B%B %H %B";
+
+  @Test
+  public void testExtractElementPlaceHolder() {
+    List<AccessLogElementExtraction> extractionList = MATCHER.extractElementPlaceholder(RAW_PATTERN);
+
+    assertEquals(4, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(2, extractionList.get(0).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(0).getAccessLogElement());
+    assertEquals(18, extractionList.get(1).getStart());
+    assertEquals(20, extractionList.get(1).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(1).getAccessLogElement());
+    assertEquals(20, extractionList.get(2).getStart());
+    assertEquals(22, extractionList.get(2).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(2).getAccessLogElement());
+    assertEquals(26, extractionList.get(3).getStart());
+    assertEquals(28, extractionList.get(3).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(3).getAccessLogElement());
+  }
+
+  @Test
+  public void testGetPlaceholderPattern() {
+    assertEquals("%B", MATCHER.getPlaceholderPattern());
+  }
+
+  @Test
+  public void getAccessLogElement() {
+    assertTrue(BytesWrittenV1Element.class.equals(MATCHER.getAccessLogElement().getClass()));
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/BytesWrittenV2MatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/BytesWrittenV2MatcherTest.java
new file mode 100644
index 0000000..d097a17
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/BytesWrittenV2MatcherTest.java
@@ -0,0 +1,46 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.BytesWrittenV2Element;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public class BytesWrittenV2MatcherTest {
+  private static final BytesWrittenV2Matcher MATCHER = new BytesWrittenV2Matcher();
+
+  private static final String RAW_PATTERN = "%b %h %{PATTERN}t %b%b %H %b";
+
+  @Test
+  public void testExtractElementPlaceHolder() {
+    List<AccessLogElementExtraction> extractionList = MATCHER.extractElementPlaceholder(RAW_PATTERN);
+
+    assertEquals(4, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(2, extractionList.get(0).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(0).getAccessLogElement());
+    assertEquals(18, extractionList.get(1).getStart());
+    assertEquals(20, extractionList.get(1).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(1).getAccessLogElement());
+    assertEquals(20, extractionList.get(2).getStart());
+    assertEquals(22, extractionList.get(2).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(2).getAccessLogElement());
+    assertEquals(26, extractionList.get(3).getStart());
+    assertEquals(28, extractionList.get(3).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(3).getAccessLogElement());
+  }
+
+  @Test
+  public void testGetPlaceholderPattern() {
+    assertEquals("%b", MATCHER.getPlaceholderPattern());
+  }
+
+  @Test
+  public void getAccessLogElement() {
+    assertTrue(BytesWrittenV2Element.class.equals(MATCHER.getAccessLogElement().getClass()));
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/CookieElementMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/CookieElementMatcherTest.java
new file mode 100644
index 0000000..da5a1cb
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/CookieElementMatcherTest.java
@@ -0,0 +1,48 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.CookieElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public class CookieElementMatcherTest {
+
+  private static final CookieElementMatcher MATCHER = new CookieElementMatcher();
+
+  @Test
+  public void extractElementPlaceholder() {
+    List<AccessLogElementExtraction> extractionList = MATCHER
+        .extractElementPlaceholder("%{header0}c %h %{yyyyMMdd HH:mm:ss zzz}t %{header1}c %b%b %H %{header2}c");
+
+    assertEquals(3, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(11, extractionList.get(0).getEnd());
+    assertEquals(41, extractionList.get(1).getStart());
+    assertEquals(52, extractionList.get(1).getEnd());
+    assertEquals(61, extractionList.get(2).getStart());
+    assertEquals(72, extractionList.get(2).getEnd());
+
+    assertEquals(CookieElement.class, extractionList.get(0).getAccessLogElement().getClass());
+    assertEquals(CookieElement.class, extractionList.get(1).getAccessLogElement().getClass());
+    assertEquals(CookieElement.class, extractionList.get(2).getAccessLogElement().getClass());
+
+    assertEquals("header0",
+        ((CookieElement) (extractionList.get(0).getAccessLogElement())).getIdentifier());
+    assertEquals("header1",
+        ((CookieElement) (extractionList.get(1).getAccessLogElement())).getIdentifier());
+    assertEquals("header2",
+        ((CookieElement) (extractionList.get(2).getAccessLogElement())).getIdentifier());
+  }
+
+  @Test
+  public void extractElementPlaceholderOnNotMatched() {
+    List<AccessLogElementExtraction> extractionList = MATCHER
+        .extractElementPlaceholder("%{header0}i %h %{yyyyMMdd HH:mm:ss zzz}t %{header1}i %b%b %H %{header2}i");
+
+    assertEquals(0, extractionList.size());
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DatetimeConfigurableMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DatetimeConfigurableMatcherTest.java
new file mode 100644
index 0000000..38ac857
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DatetimeConfigurableMatcherTest.java
@@ -0,0 +1,120 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.DatetimeConfigurableElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+import mockit.Deencapsulation;
+
+public class DatetimeConfigurableMatcherTest {
+  private static final DatetimeConfigurableMatcher MATCHER = new DatetimeConfigurableMatcher();
+
+  @Test
+  public void extractElementPlaceholderOnOnlyV1() {
+    List<AccessLogElementExtraction> extractionList = MATCHER
+        .extractElementPlaceholder("%{yyyyMMdd}t %h %{yyyyMMdd HH:mm:ss zzz}t %b%b %H %{yyyyMMdd}t");
+
+    assertEquals(3, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(12, extractionList.get(0).getEnd());
+    assertEquals(16, extractionList.get(1).getStart());
+    assertEquals(41, extractionList.get(1).getEnd());
+    assertEquals(50, extractionList.get(2).getStart());
+    assertEquals(62, extractionList.get(2).getEnd());
+
+    assertEquals(DatetimeConfigurableElement.class, extractionList.get(0).getAccessLogElement().getClass());
+    assertEquals(DatetimeConfigurableElement.class, extractionList.get(1).getAccessLogElement().getClass());
+    assertEquals(DatetimeConfigurableElement.class, extractionList.get(2).getAccessLogElement().getClass());
+
+    assertEquals("yyyyMMdd",
+        ((DatetimeConfigurableElement) (extractionList.get(0).getAccessLogElement())).getPattern());
+    assertEquals("yyyyMMdd HH:mm:ss zzz",
+        ((DatetimeConfigurableElement) (extractionList.get(1).getAccessLogElement())).getPattern());
+    assertEquals("yyyyMMdd",
+        ((DatetimeConfigurableElement) (extractionList.get(2).getAccessLogElement())).getPattern());
+  }
+
+  @Test
+  public void extractElementPlaceholderOnOnlyV2() {
+    List<AccessLogElementExtraction> extractionList = MATCHER
+        .extractElementPlaceholder(
+            "%{EEE, dd MMM yyyy HH:mm:ss zzz||zh-CN}t %h %{EEE, yy/MM/dd HH:mm:ss zzz|GMT+08|zh-CN}t %b%b %H %{EEE, dd MMM yyyy HH:mm:ss zzz|GMT-08|}t");
+
+    assertEquals(3, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(40, extractionList.get(0).getEnd());
+    assertEquals(44, extractionList.get(1).getStart());
+    assertEquals(87, extractionList.get(1).getEnd());
+    assertEquals(96, extractionList.get(2).getStart());
+    assertEquals(137, extractionList.get(2).getEnd());
+
+    assertEquals(DatetimeConfigurableElement.class, extractionList.get(0).getAccessLogElement().getClass());
+    assertEquals(DatetimeConfigurableElement.class, extractionList.get(1).getAccessLogElement().getClass());
+    assertEquals(DatetimeConfigurableElement.class, extractionList.get(2).getAccessLogElement().getClass());
+
+    assertEquals("EEE, dd MMM yyyy HH:mm:ss zzz",
+        Deencapsulation.getField(extractionList.get(0).getAccessLogElement(), "pattern"));
+    assertEquals(TimeZone.getDefault(),
+        Deencapsulation.getField(extractionList.get(0).getAccessLogElement(), "timezone"));
+    assertEquals(Locale.SIMPLIFIED_CHINESE,
+        Deencapsulation.getField(extractionList.get(0).getAccessLogElement(), "locale"));
+
+    assertEquals("EEE, yy/MM/dd HH:mm:ss zzz",
+        Deencapsulation.getField(extractionList.get(1).getAccessLogElement(), "pattern"));
+    assertEquals(TimeZone.getTimeZone("GMT+08"),
+        Deencapsulation.getField(extractionList.get(1).getAccessLogElement(), "timezone"));
+    assertEquals(Locale.SIMPLIFIED_CHINESE,
+        Deencapsulation.getField(extractionList.get(1).getAccessLogElement(), "locale"));
+
+    assertEquals("EEE, dd MMM yyyy HH:mm:ss zzz",
+        Deencapsulation.getField(extractionList.get(2).getAccessLogElement(), "pattern"));
+    assertEquals(TimeZone.getTimeZone("GMT-08"),
+        Deencapsulation.getField(extractionList.get(2).getAccessLogElement(), "timezone"));
+    assertEquals(Locale.US, Deencapsulation.getField(extractionList.get(2).getAccessLogElement(), "locale"));
+  }
+
+  @Test
+  public void extractElementPlaceholderOnV1V2Mixed() {
+    List<AccessLogElementExtraction> extractionList = MATCHER
+        .extractElementPlaceholder(
+            "%{yyyyMMdd}t %h %{yyyyMMdd HH:mm:ss zzz}t %{EEE, dd MMM yyyy HH:mm:ss zzz|GMT+08|zh-CN}t %b%b %H %{yyyyMMdd}t");
+
+    assertEquals(4, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(12, extractionList.get(0).getEnd());
+    assertEquals(16, extractionList.get(1).getStart());
+    assertEquals(41, extractionList.get(1).getEnd());
+    assertEquals(42, extractionList.get(2).getStart());
+    assertEquals(88, extractionList.get(2).getEnd());
+    assertEquals(97, extractionList.get(3).getStart());
+    assertEquals(109, extractionList.get(3).getEnd());
+
+    assertEquals(DatetimeConfigurableElement.class, extractionList.get(0).getAccessLogElement().getClass());
+    assertEquals(DatetimeConfigurableElement.class, extractionList.get(1).getAccessLogElement().getClass());
+    assertEquals(DatetimeConfigurableElement.class, extractionList.get(2).getAccessLogElement().getClass());
+    assertEquals(DatetimeConfigurableElement.class, extractionList.get(3).getAccessLogElement().getClass());
+    assertEquals("yyyyMMdd",
+        ((DatetimeConfigurableElement) (extractionList.get(0).getAccessLogElement())).getPattern());
+    assertEquals("yyyyMMdd HH:mm:ss zzz",
+        ((DatetimeConfigurableElement) (extractionList.get(1).getAccessLogElement())).getPattern());
+    assertEquals("EEE, dd MMM yyyy HH:mm:ss zzz",
+        ((DatetimeConfigurableElement) (extractionList.get(2).getAccessLogElement())).getPattern());
+    assertEquals("yyyyMMdd",
+        ((DatetimeConfigurableElement) (extractionList.get(3).getAccessLogElement())).getPattern());
+  }
+
+  @Test
+  public void extractElementPlaceholderOnNotFound() {
+    List<AccessLogElementExtraction> extractionList = MATCHER
+        .extractElementPlaceholder(
+            "%{identifier}i %h %b%b %H %{identifier}o");
+
+    assertEquals(0, extractionList.size());
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DatetimeMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DatetimeMatcherTest.java
new file mode 100644
index 0000000..799e3ec
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DatetimeMatcherTest.java
@@ -0,0 +1,46 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.DatetimeConfigurableElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public class DatetimeMatcherTest {
+  private static final DatetimeMatcher MATCHER = new DatetimeMatcher();
+
+  private static final String RAW_PATTERN = "%t %h %{PATTERN}t %t%t %H %t";
+
+  @Test
+  public void testExtractElementPlaceHolder() {
+    List<AccessLogElementExtraction> extractionList = MATCHER.extractElementPlaceholder(RAW_PATTERN);
+
+    assertEquals(4, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(2, extractionList.get(0).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(0).getAccessLogElement());
+    assertEquals(18, extractionList.get(1).getStart());
+    assertEquals(20, extractionList.get(1).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(1).getAccessLogElement());
+    assertEquals(20, extractionList.get(2).getStart());
+    assertEquals(22, extractionList.get(2).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(2).getAccessLogElement());
+    assertEquals(26, extractionList.get(3).getStart());
+    assertEquals(28, extractionList.get(3).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(3).getAccessLogElement());
+  }
+
+  @Test
+  public void testGetPlaceholderPattern() {
+    assertEquals("%t", MATCHER.getPlaceholderPattern());
+  }
+
+  @Test
+  public void getAccessLogElement() {
+    assertTrue(DatetimeConfigurableElement.class.equals(MATCHER.getAccessLogElement().getClass()));
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DurationMillisecondMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DurationMillisecondMatcherTest.java
new file mode 100644
index 0000000..1029ae1
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DurationMillisecondMatcherTest.java
@@ -0,0 +1,46 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.DurationMillisecondElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public class DurationMillisecondMatcherTest {
+  private static final DurationMillisecondMatcher MATCHER = new DurationMillisecondMatcher();
+
+  private static final String RAW_PATTERN = "%D %h %{PATTERN}t %D%D %H %D";
+
+  @Test
+  public void testExtractElementPlaceHolder() {
+    List<AccessLogElementExtraction> extractionList = MATCHER.extractElementPlaceholder(RAW_PATTERN);
+
+    assertEquals(4, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(2, extractionList.get(0).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(0).getAccessLogElement());
+    assertEquals(18, extractionList.get(1).getStart());
+    assertEquals(20, extractionList.get(1).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(1).getAccessLogElement());
+    assertEquals(20, extractionList.get(2).getStart());
+    assertEquals(22, extractionList.get(2).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(2).getAccessLogElement());
+    assertEquals(26, extractionList.get(3).getStart());
+    assertEquals(28, extractionList.get(3).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(3).getAccessLogElement());
+  }
+
+  @Test
+  public void testGetPlaceholderPattern() {
+    assertEquals("%D", MATCHER.getPlaceholderPattern());
+  }
+
+  @Test
+  public void getAccessLogElement() {
+    assertTrue(DurationMillisecondElement.class.equals(MATCHER.getAccessLogElement().getClass()));
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DurationSecondMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DurationSecondMatcherTest.java
new file mode 100644
index 0000000..bc779a8
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/DurationSecondMatcherTest.java
@@ -0,0 +1,46 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.DurationSecondElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public class DurationSecondMatcherTest {
+  private static final DurationSecondMatcher MATCHER = new DurationSecondMatcher();
+
+  private static final String RAW_PATTERN = "%T %h %{PATTERN}t %T%T %H %T";
+
+  @Test
+  public void testExtractElementPlaceHolder() {
+    List<AccessLogElementExtraction> extractionList = MATCHER.extractElementPlaceholder(RAW_PATTERN);
+
+    assertEquals(4, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(2, extractionList.get(0).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(0).getAccessLogElement());
+    assertEquals(18, extractionList.get(1).getStart());
+    assertEquals(20, extractionList.get(1).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(1).getAccessLogElement());
+    assertEquals(20, extractionList.get(2).getStart());
+    assertEquals(22, extractionList.get(2).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(2).getAccessLogElement());
+    assertEquals(26, extractionList.get(3).getStart());
+    assertEquals(28, extractionList.get(3).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(3).getAccessLogElement());
+  }
+
+  @Test
+  public void testGetPlaceholderPattern() {
+    assertEquals("%T", MATCHER.getPlaceholderPattern());
+  }
+
+  @Test
+  public void getAccessLogElement() {
+    assertTrue(DurationSecondElement.class.equals(MATCHER.getAccessLogElement().getClass()));
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/FirstLineOfRequestMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/FirstLineOfRequestMatcherTest.java
new file mode 100644
index 0000000..670bd62
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/FirstLineOfRequestMatcherTest.java
@@ -0,0 +1,46 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.FirstLineOfRequestElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public class FirstLineOfRequestMatcherTest {
+  private static final FirstLineOfRequestMatcher MATCHER = new FirstLineOfRequestMatcher();
+
+  private static final String RAW_PATTERN = "%r %h %{PATTERN}t %r%r %H %r";
+
+  @Test
+  public void testExtractElementPlaceHolder() {
+    List<AccessLogElementExtraction> extractionList = MATCHER.extractElementPlaceholder(RAW_PATTERN);
+
+    assertEquals(4, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(2, extractionList.get(0).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(0).getAccessLogElement());
+    assertEquals(18, extractionList.get(1).getStart());
+    assertEquals(20, extractionList.get(1).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(1).getAccessLogElement());
+    assertEquals(20, extractionList.get(2).getStart());
+    assertEquals(22, extractionList.get(2).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(2).getAccessLogElement());
+    assertEquals(26, extractionList.get(3).getStart());
+    assertEquals(28, extractionList.get(3).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(3).getAccessLogElement());
+  }
+
+  @Test
+  public void testGetPlaceholderPattern() {
+    assertEquals("%r", MATCHER.getPlaceholderPattern());
+  }
+
+  @Test
+  public void getAccessLogElement() {
+    assertTrue(FirstLineOfRequestElement.class.equals(MATCHER.getAccessLogElement().getClass()));
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/ImmutableAccessLogElementMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/ImmutableAccessLogElementMatcherTest.java
new file mode 100644
index 0000000..befd00c
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/ImmutableAccessLogElementMatcherTest.java
@@ -0,0 +1,64 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.AccessLogElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public class ImmutableAccessLogElementMatcherTest {
+  public static final ImmutableAccessLogElementMatcher MATCHER = new MockImmutableAccessLogElementMatcher();
+
+  @Test
+  public void testExtractElementPlaceHolder() {
+    final String rawPattern = "%m %m%m %m";
+    List<AccessLogElementExtraction> extractionList = MATCHER.extractElementPlaceholder(rawPattern);
+
+    assertEquals(4, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(2, extractionList.get(0).getEnd());
+    assertEquals(3, extractionList.get(1).getStart());
+    assertEquals(5, extractionList.get(1).getEnd());
+    assertEquals(5, extractionList.get(2).getStart());
+    assertEquals(7, extractionList.get(2).getEnd());
+    assertEquals(8, extractionList.get(3).getStart());
+    assertEquals(10, extractionList.get(3).getEnd());
+
+    assertEquals(MockImmutableAccessLogElementMatcher.ELEMENT, extractionList.get(0).getAccessLogElement());
+    assertEquals(MockImmutableAccessLogElementMatcher.ELEMENT, extractionList.get(1).getAccessLogElement());
+    assertEquals(MockImmutableAccessLogElementMatcher.ELEMENT, extractionList.get(2).getAccessLogElement());
+    assertEquals(MockImmutableAccessLogElementMatcher.ELEMENT, extractionList.get(3).getAccessLogElement());
+  }
+
+  @Test
+  public void testExtractElementPlaceHolderOnNoMatch() {
+    final String rawPattern = "%p %r%{PATTERN}tcs-status";
+    List<AccessLogElementExtraction> extractionList = MATCHER.extractElementPlaceholder(rawPattern);
+
+    assertEquals(0, extractionList.size());
+  }
+
+  public static class MockImmutableAccessLogElementMatcher extends ImmutableAccessLogElementMatcher {
+    public static final AccessLogElement ELEMENT = Mockito.mock(AccessLogElement.class);
+
+    public static final String PLACEHOLDER_PATTERN = "%m";
+
+    @Override
+    protected AccessLogElement getAccessLogElement() {
+      return ELEMENT;
+    }
+
+    @Override
+    public List<AccessLogElementExtraction> extractElementPlaceholder(String rawPattern) {
+      List<AccessLogElementExtraction> extractionList = new ArrayList<>();
+      matchElementPlaceholder(rawPattern, PLACEHOLDER_PATTERN, extractionList);
+
+      return extractionList;
+    }
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/LocalHostMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/LocalHostMatcherTest.java
new file mode 100644
index 0000000..086f461
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/LocalHostMatcherTest.java
@@ -0,0 +1,46 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.LocalHostElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public class LocalHostMatcherTest {
+  private static final LocalHostMatcher MATCHER = new LocalHostMatcher();
+
+  private static final String RAW_PATTERN = "%v %h %{PATTERN}t %v%v %H %v";
+
+  @Test
+  public void testExtractElementPlaceHolder() {
+    List<AccessLogElementExtraction> extractionList = MATCHER.extractElementPlaceholder(RAW_PATTERN);
+
+    assertEquals(4, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(2, extractionList.get(0).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(0).getAccessLogElement());
+    assertEquals(18, extractionList.get(1).getStart());
+    assertEquals(20, extractionList.get(1).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(1).getAccessLogElement());
+    assertEquals(20, extractionList.get(2).getStart());
+    assertEquals(22, extractionList.get(2).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(2).getAccessLogElement());
+    assertEquals(26, extractionList.get(3).getStart());
+    assertEquals(28, extractionList.get(3).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(3).getAccessLogElement());
+  }
+
+  @Test
+  public void testGetPlaceholderPattern() {
+    assertEquals("%v", MATCHER.getPlaceholderPattern());
+  }
+
+  @Test
+  public void getAccessLogElement() {
+    assertTrue(LocalHostElement.class.equals(MATCHER.getAccessLogElement().getClass()));
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/LocalPortMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/LocalPortMatcherTest.java
new file mode 100644
index 0000000..6970fee
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/LocalPortMatcherTest.java
@@ -0,0 +1,46 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.LocalPortElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public class LocalPortMatcherTest {
+  private static final LocalPortMatcher MATCHER = new LocalPortMatcher();
+
+  private static final String RAW_PATTERN = "%p %h %{PATTERN}t %p%p %H %p";
+
+  @Test
+  public void testExtractElementPlaceHolder() {
+    List<AccessLogElementExtraction> extractionList = MATCHER.extractElementPlaceholder(RAW_PATTERN);
+
+    assertEquals(4, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(2, extractionList.get(0).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(0).getAccessLogElement());
+    assertEquals(18, extractionList.get(1).getStart());
+    assertEquals(20, extractionList.get(1).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(1).getAccessLogElement());
+    assertEquals(20, extractionList.get(2).getStart());
+    assertEquals(22, extractionList.get(2).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(2).getAccessLogElement());
+    assertEquals(26, extractionList.get(3).getStart());
+    assertEquals(28, extractionList.get(3).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(3).getAccessLogElement());
+  }
+
+  @Test
+  public void testGetPlaceholderPattern() {
+    assertEquals("%p", MATCHER.getPlaceholderPattern());
+  }
+
+  @Test
+  public void getAccessLogElement() {
+    assertTrue(LocalPortElement.class.equals(MATCHER.getAccessLogElement().getClass()));
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/MethodMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/MethodMatcherTest.java
new file mode 100644
index 0000000..b0568ea
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/MethodMatcherTest.java
@@ -0,0 +1,30 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.impl.DefaultAccessLogPatternParser;
+
+public class MethodMatcherTest {
+  private static final MethodMatcher MATCHER = new MethodMatcher();
+
+  private static final String RAW_PATTERN = "cs-method %s cs-method %T %m";
+
+  @Test
+  public void extractElementPlaceHolder() {
+    List<AccessLogElementExtraction> extractionList = MATCHER.extractElementPlaceholder(RAW_PATTERN);
+    Collections.sort(extractionList, DefaultAccessLogPatternParser.ACCESS_LOG_ELEMENT_EXTRACTION_COMPARATOR);
+    assertEquals(3, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(9, extractionList.get(0).getEnd());
+    assertEquals(13, extractionList.get(1).getStart());
+    assertEquals(22, extractionList.get(1).getEnd());
+    assertEquals(26, extractionList.get(2).getStart());
+    assertEquals(28, extractionList.get(2).getEnd());
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/QueryOnlyMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/QueryOnlyMatcherTest.java
new file mode 100644
index 0000000..a149281
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/QueryOnlyMatcherTest.java
@@ -0,0 +1,26 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.QueryOnlyElement;
+
+public class QueryOnlyMatcherTest {
+
+  public static final QueryOnlyMatcher MATCHER = new QueryOnlyMatcher();
+
+  @Test
+  public void getPlaceholderPatterns() {
+    String[] patterns = MATCHER.getPlaceholderPatterns();
+
+    assertEquals(2, patterns.length);
+    assertEquals("%q", patterns[0]);
+    assertEquals("cs-uri-query", patterns[1]);
+  }
+
+  @Test
+  public void getAccessLogElement() {
+    assertEquals(QueryOnlyElement.class, MATCHER.getAccessLogElement().getClass());
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/RemoteHostMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/RemoteHostMatcherTest.java
new file mode 100644
index 0000000..0b3209e
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/RemoteHostMatcherTest.java
@@ -0,0 +1,49 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.RemoteHostElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public class RemoteHostMatcherTest {
+  private static final RemoteHostMatcher MATCHER = new RemoteHostMatcher();
+
+  private static final String RAW_PATTERN = "%h %h %{PATTERN}t %h%h %H %h";
+
+  @Test
+  public void testExtractElementPlaceHolder() {
+    List<AccessLogElementExtraction> extractionList = MATCHER.extractElementPlaceholder(RAW_PATTERN);
+
+    assertEquals(5, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(2, extractionList.get(0).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(0).getAccessLogElement());
+    assertEquals(3, extractionList.get(1).getStart());
+    assertEquals(5, extractionList.get(1).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(1).getAccessLogElement());
+    assertEquals(18, extractionList.get(2).getStart());
+    assertEquals(20, extractionList.get(2).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(2).getAccessLogElement());
+    assertEquals(20, extractionList.get(3).getStart());
+    assertEquals(22, extractionList.get(3).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(3).getAccessLogElement());
+    assertEquals(26, extractionList.get(4).getStart());
+    assertEquals(28, extractionList.get(4).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(4).getAccessLogElement());
+  }
+
+  @Test
+  public void testGetPlaceholderPattern() {
+    assertEquals("%h", MATCHER.getPlaceholderPattern());
+  }
+
+  @Test
+  public void getAccessLogElement() {
+    assertTrue(RemoteHostElement.class.equals(MATCHER.getAccessLogElement().getClass()));
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/RequestHeaderElementMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/RequestHeaderElementMatcherTest.java
new file mode 100644
index 0000000..b7733fa
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/RequestHeaderElementMatcherTest.java
@@ -0,0 +1,47 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.RequestHeaderElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public class RequestHeaderElementMatcherTest {
+  private static final RequestHeaderElementMatcher MATCHER = new RequestHeaderElementMatcher();
+
+  @Test
+  public void extractElementPlaceholder() {
+    List<AccessLogElementExtraction> extractionList = MATCHER
+        .extractElementPlaceholder("%{header0}i %h %{yyyyMMdd HH:mm:ss zzz}t %{header1}i %b%b %H %{header2}i");
+
+    assertEquals(3, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(11, extractionList.get(0).getEnd());
+    assertEquals(41, extractionList.get(1).getStart());
+    assertEquals(52, extractionList.get(1).getEnd());
+    assertEquals(61, extractionList.get(2).getStart());
+    assertEquals(72, extractionList.get(2).getEnd());
+
+    assertEquals(RequestHeaderElement.class, extractionList.get(0).getAccessLogElement().getClass());
+    assertEquals(RequestHeaderElement.class, extractionList.get(1).getAccessLogElement().getClass());
+    assertEquals(RequestHeaderElement.class, extractionList.get(2).getAccessLogElement().getClass());
+
+    assertEquals("header0",
+        ((RequestHeaderElement) (extractionList.get(0).getAccessLogElement())).getIdentifier());
+    assertEquals("header1",
+        ((RequestHeaderElement) (extractionList.get(1).getAccessLogElement())).getIdentifier());
+    assertEquals("header2",
+        ((RequestHeaderElement) (extractionList.get(2).getAccessLogElement())).getIdentifier());
+  }
+
+  @Test
+  public void extractElementPlaceholderOnNotMatched() {
+    List<AccessLogElementExtraction> extractionList = MATCHER
+        .extractElementPlaceholder("%{header0}o %h %{yyyyMMdd HH:mm:ss zzz}t %{header1}o %b%b %H %{header2}o");
+
+    assertEquals(0, extractionList.size());
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/ResponseHeaderElementMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/ResponseHeaderElementMatcherTest.java
new file mode 100644
index 0000000..e7cd03f
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/ResponseHeaderElementMatcherTest.java
@@ -0,0 +1,48 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.ResponseHeaderElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public class ResponseHeaderElementMatcherTest {
+
+  private static final ResponseHeaderElementMatcher MATCHER = new ResponseHeaderElementMatcher();
+
+  @Test
+  public void extractElementPlaceholder() {
+    List<AccessLogElementExtraction> extractionList = MATCHER
+        .extractElementPlaceholder("%{header0}o %h %{yyyyMMdd HH:mm:ss zzz}t %{header1}o %b%b %H %{header2}o");
+
+    assertEquals(3, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(11, extractionList.get(0).getEnd());
+    assertEquals(41, extractionList.get(1).getStart());
+    assertEquals(52, extractionList.get(1).getEnd());
+    assertEquals(61, extractionList.get(2).getStart());
+    assertEquals(72, extractionList.get(2).getEnd());
+
+    assertEquals(ResponseHeaderElement.class, extractionList.get(0).getAccessLogElement().getClass());
+    assertEquals(ResponseHeaderElement.class, extractionList.get(1).getAccessLogElement().getClass());
+    assertEquals(ResponseHeaderElement.class, extractionList.get(2).getAccessLogElement().getClass());
+
+    assertEquals("header0",
+        ((ResponseHeaderElement) (extractionList.get(0).getAccessLogElement())).getIdentifier());
+    assertEquals("header1",
+        ((ResponseHeaderElement) (extractionList.get(1).getAccessLogElement())).getIdentifier());
+    assertEquals("header2",
+        ((ResponseHeaderElement) (extractionList.get(2).getAccessLogElement())).getIdentifier());
+  }
+
+  @Test
+  public void extractElementPlaceholderOnNotMatched() {
+    List<AccessLogElementExtraction> extractionList = MATCHER
+        .extractElementPlaceholder("%{header0}i %h %{yyyyMMdd HH:mm:ss zzz}t %{header1}i %b%b %H %{header2}i");
+
+    assertEquals(0, extractionList.size());
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/StatusMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/StatusMatcherTest.java
new file mode 100644
index 0000000..5976104
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/StatusMatcherTest.java
@@ -0,0 +1,24 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.StatusElement;
+
+public class StatusMatcherTest {
+  private static final StatusMatcher MATCHER = new StatusMatcher();
+
+  @Test
+  public void getPlaceholderPatterns() {
+    String[] patterns = MATCHER.getPlaceholderPatterns();
+    assertEquals(2, patterns.length);
+    assertEquals("%s", patterns[0]);
+    assertEquals("cs-status", patterns[1]);
+  }
+
+  @Test
+  public void getAccessLogElement() {
+    assertEquals(StatusElement.class, MATCHER.getAccessLogElement().getClass());
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/UriPathIncludeQueryMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/UriPathIncludeQueryMatcherTest.java
new file mode 100644
index 0000000..8de4e0d
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/UriPathIncludeQueryMatcherTest.java
@@ -0,0 +1,35 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.UriPathIncludeQueryElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public class UriPathIncludeQueryMatcherTest {
+  private static final UriPathIncludeQueryMatcher MATCHER = new UriPathIncludeQueryMatcher();
+
+  private static final String RAW_PATTERN = "cs-uri %h %{PATTERN}t cs-urics-uri %H cs-uri-query cs-uri";
+
+  @Test
+  public void testExtractElementPlaceHolder() {
+    List<AccessLogElementExtraction> extractionList = MATCHER.extractElementPlaceholder(RAW_PATTERN);
+
+    assertEquals(4, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(6, extractionList.get(0).getEnd());
+    assertEquals(UriPathIncludeQueryElement.class, extractionList.get(0).getAccessLogElement().getClass());
+    assertEquals(22, extractionList.get(1).getStart());
+    assertEquals(28, extractionList.get(1).getEnd());
+    assertEquals(UriPathIncludeQueryElement.class, extractionList.get(1).getAccessLogElement().getClass());
+    assertEquals(28, extractionList.get(2).getStart());
+    assertEquals(34, extractionList.get(2).getEnd());
+    assertEquals(UriPathIncludeQueryElement.class, extractionList.get(2).getAccessLogElement().getClass());
+    assertEquals(51, extractionList.get(3).getStart());
+    assertEquals(57, extractionList.get(3).getEnd());
+    assertEquals(UriPathIncludeQueryElement.class, extractionList.get(3).getAccessLogElement().getClass());
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/UriPathOnlyMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/UriPathOnlyMatcherTest.java
new file mode 100644
index 0000000..568f869
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/UriPathOnlyMatcherTest.java
@@ -0,0 +1,26 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.UriPathOnlyElement;
+
+public class UriPathOnlyMatcherTest {
+
+  public static final UriPathOnlyMatcher MATCHER = new UriPathOnlyMatcher();
+
+  @Test
+  public void getPlaceholderPatterns() {
+    String[] patterns = MATCHER.getPlaceholderPatterns();
+
+    assertEquals(2, patterns.length);
+    assertEquals("%U", patterns[0]);
+    assertEquals("cs-uri-stem", patterns[1]);
+  }
+
+  @Test
+  public void getAccessLogElement() {
+    assertEquals(UriPathOnlyElement.class, MATCHER.getAccessLogElement().getClass());
+  }
+}
\ No newline at end of file
diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/VersionOrProtocolMatcherTest.java b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/VersionOrProtocolMatcherTest.java
new file mode 100644
index 0000000..cb74b99
--- /dev/null
+++ b/transports/transport-rest/transport-rest-vertx/src/test/java/io/servicecomb/transport/rest/vertx/accesslog/parser/matcher/impl/VersionOrProtocolMatcherTest.java
@@ -0,0 +1,49 @@
+package io.servicecomb.transport.rest.vertx.accesslog.parser.matcher.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import io.servicecomb.transport.rest.vertx.accesslog.element.impl.VersionOrProtocolElement;
+import io.servicecomb.transport.rest.vertx.accesslog.parser.AccessLogElementExtraction;
+
+public class VersionOrProtocolMatcherTest {
+  private static final VersionOrProtocolMatcher MATCHER = new VersionOrProtocolMatcher();
+
+  private static final String RAW_PATTERN = "%H %h %{PATTERN}t %H%H %H %H";
+
+  @Test
+  public void testExtractElementPlaceHolder() {
+    List<AccessLogElementExtraction> extractionList = MATCHER.extractElementPlaceholder(RAW_PATTERN);
+
+    assertEquals(5, extractionList.size());
+    assertEquals(0, extractionList.get(0).getStart());
+    assertEquals(2, extractionList.get(0).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(0).getAccessLogElement());
+    assertEquals(18, extractionList.get(1).getStart());
+    assertEquals(20, extractionList.get(1).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(1).getAccessLogElement());
+    assertEquals(20, extractionList.get(2).getStart());
+    assertEquals(22, extractionList.get(2).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(2).getAccessLogElement());
+    assertEquals(23, extractionList.get(3).getStart());
+    assertEquals(25, extractionList.get(3).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(3).getAccessLogElement());
+    assertEquals(26, extractionList.get(4).getStart());
+    assertEquals(28, extractionList.get(4).getEnd());
+    assertEquals(MATCHER.getAccessLogElement(), extractionList.get(4).getAccessLogElement());
+  }
+
+  @Test
+  public void testGetPlaceholderPattern() {
+    assertEquals("%H", MATCHER.getPlaceholderPattern());
+  }
+
+  @Test
+  public void getAccessLogElement() {
+    assertTrue(VersionOrProtocolElement.class.equals(MATCHER.getAccessLogElement().getClass()));
+  }
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
"commits@servicecomb.apache.org" <co...@servicecomb.apache.org>.