You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2020/03/20 07:55:58 UTC

[servicecomb-java-chassis] 03/04: [SCB-1796] add ut

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

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

commit 8fd8241c51b33004bac19667d43f45166c822707
Author: heyile <25...@qq.com>
AuthorDate: Tue Mar 17 21:47:11 2020 +0800

    [SCB-1796]  add ut
---
 .../servicecomb/foundation/log/core/README.md      |   5 +-
 .../log/core/element/impl/HttpMethodItem.java      |   2 +-
 .../core/element/impl/InvocationContextItem.java   |   4 +-
 .../log/core/element/impl/QueryStringItem.java     |   2 +-
 .../log/core/element/impl/RemoteHostItem.java      |   8 +-
 .../log/core/element/impl/RequestHeaderItem.java   |   6 +-
 .../log/core/element/impl/RequestProtocolItem.java |   6 +-
 .../log/core/element/impl/ResponseSizeItem.java    |   4 +-
 .../log/core/element/impl/TraceIdItem.java         |  13 +-
 .../log/core/element/impl/UrlPathItem.java         |   2 +-
 .../core/element/impl/UrlPathWithQueryItem.java    |   8 +-
 .../parser/impl/VertxRestLogPatternParser.java     |   4 +-
 .../foundation/log/core/LogConfigTest.java         |  44 +++
 .../foundation/log/core/LogGeneratorTest.java      |  96 ++++++
 .../log/core/element/impl/CookieItemTest.java      | 177 ++++++++++
 .../element/impl/DatetimeConfigurableItemTest.java | 215 ++++++++++++
 .../element/impl/DurationMillisecondItemTest.java  |  71 ++++
 .../core/element/impl/DurationSecondItemTest.java  | 101 ++++++
 .../element/impl/FirstLineOfRequestItemTest.java   | 110 +++++++
 .../log/core/element/impl/HttpMethodItemTest.java  | 134 ++++++++
 .../log/core/element/impl/HttpStatusItemTest.java  | 102 ++++++
 .../element/impl/InvocationContextItemTest.java    | 151 +++++++++
 .../log/core/element/impl/LocalHostItemTest.java   | 120 +++++++
 .../log/core/element/impl/LocalPortItemTest.java   |  90 +++++
 .../log/core/element/impl/PlainTextItemTest.java}  |  43 ++-
 .../log/core/element/impl/QueryStringItemTest.java | 157 +++++++++
 .../log/core/element/impl/RemoteHostItemTest.java  | 162 +++++++++
 .../core/element/impl/RequestHeaderItemTest.java   | 141 ++++++++
 .../core/element/impl/RequestProtocolItemTest.java | 124 +++++++
 .../core/element/impl/ResponseHeaderItemTest.java  | 156 +++++++++
 .../core/element/impl/ResponseSizeItemTest.java    |  87 +++++
 .../log/core/element/impl/TraceIdItemTest.java     | 160 +++++++++
 .../log/core/element/impl/TransportItemTest.java   | 109 +++++++
 .../log/core/element/impl/UrlPathItemTest.java     | 156 +++++++++
 .../element/impl/UrlPathWithQueryItemTest.java     | 125 +++++++
 .../element/impl/UserDefinedAccessLogItem.java}    |  29 +-
 .../impl/UserDefinedAccessLogItemLowPriority.java} |  26 +-
 .../TestCompositeExtendedAccessLogItemMeta.java    |  39 +++
 .../impl/TestSingleExtendedAccessLogItemMeta.java  |  27 ++
 .../impl/VertxRestAccessLogPatternParserTest.java  | 362 +++++++++++++++++++++
 ...foundation.log.core.parser.VertxRestLogItemMeta |  19 ++
 41 files changed, 3321 insertions(+), 76 deletions(-)

diff --git a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/README.md b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/README.md
index 0826dbe..b9e72c2 100644
--- a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/README.md
+++ b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/README.md
@@ -5,9 +5,12 @@
 To enable access log printing, you can config access log in microservice.yaml like below:
 ```yaml
 servicecomb:
+  outlog:
+    enabled: true
+    pattern: "%h %SCB-transport - - %t %r %s %D"
   accesslog:
     enabled: true
-    pattern: "%h - - %t %r %s %B"
+    pattern: "%h - - %t %r %s %B %D"
 ```
 
 ***Access Log Configurations in microservice.yaml***
diff --git a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/HttpMethodItem.java b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/HttpMethodItem.java
index 5303b7a..521d770 100644
--- a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/HttpMethodItem.java
+++ b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/HttpMethodItem.java
@@ -49,7 +49,7 @@ public class HttpMethodItem implements LogItem<RoutingContext> {
   @Override
   public void appendFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) {
     OperationMeta operationMeta = finishEvent.getInvocation().getOperationMeta();
-    if (!StringUtils.isEmpty(operationMeta.getHttpMethod())) {
+    if (operationMeta != null && !StringUtils.isEmpty(operationMeta.getHttpMethod())) {
       builder.append(operationMeta.getHttpMethod());
       return;
     }
diff --git a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/InvocationContextItem.java b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/InvocationContextItem.java
index 76c8150..483faff 100644
--- a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/InvocationContextItem.java
+++ b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/InvocationContextItem.java
@@ -50,7 +50,9 @@ public class InvocationContextItem implements LogItem<RoutingContext> {
 
   @Override
   public void appendFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) {
-    if (StringUtils.isEmpty(finishEvent.getInvocation().getContext().get(varName))) {
+    Invocation invocation = finishEvent.getInvocation();
+    if (null == invocation || invocation.getContext() == null
+        || StringUtils.isEmpty(finishEvent.getInvocation().getContext().get(varName))) {
       builder.append(NOT_FOUND);
       return;
     }
diff --git a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/QueryStringItem.java b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/QueryStringItem.java
index efb35dd..b20ea8a 100644
--- a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/QueryStringItem.java
+++ b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/QueryStringItem.java
@@ -34,7 +34,7 @@ public class QueryStringItem implements LogItem<RoutingContext> {
   @Override
   public void appendFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) {
     HttpServerRequest request = accessLogEvent.getRoutingContext().request();
-    if (null == request || null == request.query()) {
+    if (null == request || StringUtils.isEmpty(request.query())) {
       builder.append(EMPTY_RESULT);
       return;
     }
diff --git a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RemoteHostItem.java b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RemoteHostItem.java
index 0334009..a53152b 100644
--- a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RemoteHostItem.java
+++ b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RemoteHostItem.java
@@ -17,7 +17,7 @@
 
 package org.apache.servicecomb.foundation.log.core.element.impl;
 
-
+import org.apache.commons.lang3.StringUtils;
 import org.apache.servicecomb.core.Endpoint;
 import org.apache.servicecomb.core.event.InvocationFinishEvent;
 import org.apache.servicecomb.core.event.ServerAccessLogEvent;
@@ -34,7 +34,8 @@ public class RemoteHostItem implements LogItem<RoutingContext> {
   @Override
   public void appendFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) {
     HttpServerRequest request = accessLogEvent.getRoutingContext().request();
-    if (null == request || null == request.remoteAddress()) {
+    if (null == request || null == request.remoteAddress()
+        || StringUtils.isEmpty(request.remoteAddress().host())) {
       builder.append(EMPTY_RESULT);
       return;
     }
@@ -44,7 +45,8 @@ public class RemoteHostItem implements LogItem<RoutingContext> {
   @Override
   public void appendFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) {
     Endpoint endpoint = clientLogEvent.getInvocation().getEndpoint();
-    if (null == endpoint || null == endpoint.getAddress()) {
+    if (null == endpoint || null == endpoint.getAddress()
+        || StringUtils.isEmpty(((URIEndpointObject) endpoint.getAddress()).getHostOrIp())) {
       builder.append(EMPTY_RESULT);
       return;
     }
diff --git a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RequestHeaderItem.java b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RequestHeaderItem.java
index 3ceb1ab..305493e 100644
--- a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RequestHeaderItem.java
+++ b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RequestHeaderItem.java
@@ -50,10 +50,10 @@ public class RequestHeaderItem implements LogItem<RoutingContext> {
   @Override
   public void appendFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) {
     RestClientRequestImpl restRequestImpl = (RestClientRequestImpl) clientLogEvent.getInvocation().getHandlerContext()
-      .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT);
+        .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT);
     if (null == restRequestImpl || null == restRequestImpl.getRequest()
-      || null == restRequestImpl.getRequest().headers()
-      || StringUtils.isEmpty(restRequestImpl.getRequest().headers().get(varName))) {
+        || null == restRequestImpl.getRequest().headers()
+        || StringUtils.isEmpty(restRequestImpl.getRequest().headers().get(varName))) {
       builder.append(RESULT_NOT_FOUND);
       return;
     }
diff --git a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RequestProtocolItem.java b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RequestProtocolItem.java
index f0ecd8e..22d9216 100644
--- a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RequestProtocolItem.java
+++ b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RequestProtocolItem.java
@@ -45,7 +45,11 @@ public class RequestProtocolItem implements LogItem<RoutingContext> {
   @Override
   public void appendFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) {
     Endpoint endpoint = finishEvent.getInvocation().getEndpoint();
-    builder.append(((URIEndpointObject) endpoint.getAddress()).isSslEnabled() ? "https" : "http");
+    if (endpoint == null || endpoint.getAddress() == null) {
+      builder.append(EMPTY_RESULT);
+      return;
+    }
+    builder.append(((URIEndpointObject) endpoint.getAddress()).isSslEnabled() ? "HTTPS" : "HTTP");
   }
 
   private String getStringVersion(HttpVersion version) {
diff --git a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/ResponseSizeItem.java b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/ResponseSizeItem.java
index c9bff18..1e00309 100644
--- a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/ResponseSizeItem.java
+++ b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/ResponseSizeItem.java
@@ -44,7 +44,9 @@ public class ResponseSizeItem implements LogItem<RoutingContext> {
 
   @Override
   public void appendFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) {
-    //client do not know how to calculate is write..
+    /**
+     * client do not know how to calculate is right, maybe Object#toString().length
+     */
     builder.append(zeroBytes);
   }
 
diff --git a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/TraceIdItem.java b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/TraceIdItem.java
index 5c35e92..e1b2761 100644
--- a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/TraceIdItem.java
+++ b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/TraceIdItem.java
@@ -17,10 +17,11 @@
 
 package org.apache.servicecomb.foundation.log.core.element.impl;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.servicecomb.core.Const;
+import org.apache.servicecomb.core.Invocation;
 import org.apache.servicecomb.core.event.InvocationFinishEvent;
 import org.apache.servicecomb.core.event.ServerAccessLogEvent;
-import org.springframework.util.StringUtils;
 
 public class TraceIdItem extends InvocationContextItem {
 
@@ -36,13 +37,17 @@ public class TraceIdItem extends InvocationContextItem {
     if (StringUtils.isEmpty(traceId)) {
       traceId = accessLogEvent.getRoutingContext().request().getHeader(TRACE_ID);
     }
-
     builder.append(StringUtils.isEmpty(traceId) ? InvocationContextItem.NOT_FOUND : traceId);
   }
 
   @Override
   public void appendFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) {
-    String traceId = finishEvent.getInvocation().getContext().get(TRACE_ID);
-    builder.append(StringUtils.isEmpty(traceId) ? InvocationContextItem.NOT_FOUND : traceId);
+    Invocation invocation = finishEvent.getInvocation();
+    if (invocation == null || invocation.getContext() == null
+        || StringUtils.isEmpty(invocation.getContext().get(TRACE_ID))) {
+      builder.append(InvocationContextItem.NOT_FOUND);
+      return;
+    }
+    builder.append(invocation.getContext().get(TRACE_ID));
   }
 }
diff --git a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/UrlPathItem.java b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/UrlPathItem.java
index a44d72d..bf6b45a 100644
--- a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/UrlPathItem.java
+++ b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/UrlPathItem.java
@@ -48,7 +48,7 @@ public class UrlPathItem implements LogItem<RoutingContext> {
   public void appendFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) {
     OperationMeta operationMeta = finishEvent.getInvocation().getOperationMeta();
     SchemaMeta schemaMeta = finishEvent.getInvocation().getSchemaMeta();
-    if (operationMeta != null && schemaMeta != null) {
+    if (operationMeta != null && schemaMeta != null && schemaMeta.getSwagger() != null) {
       builder.append(schemaMeta.getSwagger().getBasePath()).append(operationMeta.getOperationPath());
       return;
     }
diff --git a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/UrlPathWithQueryItem.java b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/UrlPathWithQueryItem.java
index febc7f6..8465677 100644
--- a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/UrlPathWithQueryItem.java
+++ b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/UrlPathWithQueryItem.java
@@ -19,6 +19,7 @@ package org.apache.servicecomb.foundation.log.core.element.impl;
 
 import static org.apache.servicecomb.common.rest.RestConst.REST_CLIENT_REQUEST_PATH;
 
+import org.apache.servicecomb.core.Invocation;
 import org.apache.servicecomb.core.event.InvocationFinishEvent;
 import org.apache.servicecomb.core.event.ServerAccessLogEvent;
 import org.apache.servicecomb.foundation.log.core.element.LogItem;
@@ -43,11 +44,12 @@ public class UrlPathWithQueryItem implements LogItem<RoutingContext> {
 
   @Override
   public void appendFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) {
-    Object path = finishEvent.getInvocation().getLocalContext(REST_CLIENT_REQUEST_PATH);
-    if (null == path || StringUtils.isEmpty(path.toString())) {
+    Invocation invocation = finishEvent.getInvocation();
+    if (null == invocation || null == invocation.getLocalContext(REST_CLIENT_REQUEST_PATH)
+        || StringUtils.isEmpty(invocation.getLocalContext(REST_CLIENT_REQUEST_PATH).toString())) {
       builder.append(EMPTY_RESULT);
       return;
     }
-    builder.append(path.toString());
+    builder.append(invocation.getLocalContext(REST_CLIENT_REQUEST_PATH).toString());
   }
 }
diff --git a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/parser/impl/VertxRestLogPatternParser.java b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/parser/impl/VertxRestLogPatternParser.java
index 40e7438..c077d76 100644
--- a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/parser/impl/VertxRestLogPatternParser.java
+++ b/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/parser/impl/VertxRestLogPatternParser.java
@@ -63,7 +63,7 @@ public class VertxRestLogPatternParser implements LogPatternParser<RoutingContex
   private List<VertxRestLogItemMeta> metaList = new ArrayList<>();
 
   public VertxRestLogPatternParser() {
-    List<VertxRestLogItemMeta> loadedMeta = loadVertxRestAccessLogItemMeta();
+    List<VertxRestLogItemMeta> loadedMeta = loadVertxRestLogItemMeta();
     if (null == loadedMeta || loadedMeta.isEmpty()) {
       LOGGER.error("cannot load AccessLogItemMeta!");
       throw new IllegalStateException("cannot load AccessLogItemMeta!");
@@ -78,7 +78,7 @@ public class VertxRestLogPatternParser implements LogPatternParser<RoutingContex
     sortAccessLogItemMeta(this.metaList);
   }
 
-  private List<VertxRestLogItemMeta> loadVertxRestAccessLogItemMeta() {
+  private List<VertxRestLogItemMeta> loadVertxRestLogItemMeta() {
     return SPIServiceUtils.getOrLoadSortedService(VertxRestLogItemMeta.class);
   }
 
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/LogConfigTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/LogConfigTest.java
new file mode 100644
index 0000000..431a800
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/LogConfigTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import org.apache.servicecomb.foundation.log.LogConfig;
+import org.junit.Test;
+
+public class LogConfigTest {
+
+  @Test
+  public void getAccessLogEnabled() {
+    boolean serverEnabled = LogConfig.INSTANCE.isServerLogEnabled();
+    boolean clientEnabled = LogConfig.INSTANCE.isClientLogEnabled();
+    assertFalse(serverEnabled);
+    assertFalse(clientEnabled);
+  }
+
+  @Test
+  public void getAccesslogPattern() {
+    String clientLogPattern = LogConfig.INSTANCE.getClientLogPattern();
+    String serverLogPattern = LogConfig.INSTANCE.getServerLogPattern();
+
+    assertEquals("%h - - %t %r %s %B %D", serverLogPattern);
+    assertEquals("%h %SCB-transport - - %t %r %s %D", clientLogPattern);
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/LogGeneratorTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/LogGeneratorTest.java
new file mode 100644
index 0000000..5682f2b
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/LogGeneratorTest.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.definition.OperationMeta;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.apache.servicecomb.core.invocation.InvocationStageTrace;
+import org.apache.servicecomb.foundation.log.core.element.LogItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.DatetimeConfigurableItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.HttpMethodItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.PlainTextItem;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.core.http.HttpMethod;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.ext.web.RoutingContext;
+import mockit.Deencapsulation;
+
+public class LogGeneratorTest {
+
+  private static final LogGenerator LOG_GENERATOR = new LogGenerator("%m - %t");
+
+  @Test
+  public void testConstructor() {
+    LogItem<RoutingContext>[] elements = Deencapsulation.getField(LOG_GENERATOR, "logItems");
+    assertEquals(3, elements.length);
+    assertEquals(HttpMethodItem.class, elements[0].getClass());
+    assertEquals(PlainTextItem.class, elements[1].getClass());
+    assertEquals(DatetimeConfigurableItem.class, elements[2].getClass());
+  }
+
+  @Test
+  public void testServerLog() {
+    RoutingContext context = Mockito.mock(RoutingContext.class);
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    long startMillisecond = 1416863450581L;
+    ServerAccessLogEvent serverAccessLogEvent = new ServerAccessLogEvent();
+    serverAccessLogEvent.setMilliStartTime(startMillisecond).setRoutingContext(context);
+    SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DatetimeConfigurableItem.DEFAULT_DATETIME_PATTERN,
+        DatetimeConfigurableItem.DEFAULT_LOCALE);
+    simpleDateFormat.setTimeZone(TimeZone.getDefault());
+
+    when(context.request()).thenReturn(request);
+    when(request.method()).thenReturn(HttpMethod.DELETE);
+
+    String log = LOG_GENERATOR.generateServerLog(serverAccessLogEvent);
+    Assert.assertEquals("DELETE" + " - " + simpleDateFormat.format(startMillisecond), log);
+  }
+
+  @Test
+  public void testClientLog() {
+    Invocation invocation = Mockito.mock(Invocation.class);
+    InvocationStageTrace stageTrace = Mockito.mock(InvocationStageTrace.class);
+    OperationMeta operationMeta = Mockito.mock(OperationMeta.class);
+    long startMillisecond = 1416863450581L;
+    when(stageTrace.getStartSend()).thenReturn(0L);
+    when(stageTrace.getStart()).thenReturn(0L);
+    when(stageTrace.getFinish()).thenReturn(0L);
+    when(stageTrace.getStartCurrentTime()).thenReturn(startMillisecond);
+    when(invocation.getOperationMeta()).thenReturn(operationMeta);
+    when(invocation.getInvocationStageTrace()).thenReturn(stageTrace);
+
+    InvocationFinishEvent finishEvent = new InvocationFinishEvent(invocation, null);
+    SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DatetimeConfigurableItem.DEFAULT_DATETIME_PATTERN,
+        DatetimeConfigurableItem.DEFAULT_LOCALE);
+    simpleDateFormat.setTimeZone(TimeZone.getDefault());
+    when(operationMeta.getHttpMethod()).thenReturn(HttpMethod.DELETE.toString());
+    String log = LOG_GENERATOR.generateClientLog(finishEvent);
+    Assert.assertEquals("DELETE" + " - " + simpleDateFormat.format(startMillisecond), log);
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/CookieItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/CookieItemTest.java
new file mode 100644
index 0000000..f31f711
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/CookieItemTest.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.servicecomb.common.rest.RestConst;
+import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.core.http.Cookie;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.impl.CookieImpl;
+
+public class CookieItemTest {
+
+  public static final String COOKIE_NAME = "cookieName";
+
+  public static final String COOKIE_VALUE = "cookieVALUE";
+
+  private static final CookieItem ELEMENT = new CookieItem(COOKIE_NAME);
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext mockContext;
+
+  private Invocation invocation;
+
+  private RestClientRequestImpl restClientRequest;
+
+  @Before
+  public void initStrBuilder() {
+    mockContext = Mockito.mock(RoutingContext.class);
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    invocation = Mockito.mock(Invocation.class);
+    restClientRequest = Mockito.mock(RestClientRequestImpl.class);
+
+    accessLogEvent = new ServerAccessLogEvent();
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void serverFormattedElement() {
+    HashMap<String, Cookie> cookieSet = new HashMap<>();
+    CookieImpl cookie = new CookieImpl(COOKIE_NAME, COOKIE_VALUE);
+
+    cookieSet.put(cookie.getName(), cookie);
+    when(mockContext.cookieCount()).thenReturn(1);
+    when(mockContext.cookieMap()).thenReturn(cookieSet);
+    accessLogEvent.setRoutingContext(mockContext);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertEquals(COOKIE_VALUE, strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElement() {
+    Map<String, Object> handlerMap = new HashMap<>();
+    handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest);
+
+    Map<String, String> cookieMap = new HashMap<>();
+    cookieMap.put(COOKIE_NAME, COOKIE_VALUE);
+
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getHandlerContext()).thenReturn(handlerMap);
+    when(restClientRequest.getCookieMap()).thenReturn(cookieMap);
+
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals(COOKIE_VALUE, strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnCookieCountIsZero() {
+    HashMap<String, Cookie> cookieSet = new HashMap<>();
+    Mockito.when(mockContext.cookieCount()).thenReturn(0);
+    Mockito.when(mockContext.cookieMap()).thenReturn(cookieSet);
+    accessLogEvent.setRoutingContext(mockContext);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnCookieCountIsZero() {
+    Map<String, Object> handlerMap = new HashMap<>();
+    handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest);
+    Map<String, String> cookieMap = new HashMap<>();
+
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getHandlerContext()).thenReturn(handlerMap);
+    when(restClientRequest.getCookieMap()).thenReturn(cookieMap);
+
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+
+
+  @Test
+  public void serverFormattedElementOnCookieSetIsNull() {
+    Mockito.when(mockContext.cookieCount()).thenReturn(1);
+    Mockito.when(mockContext.cookieMap()).thenReturn(null);
+    accessLogEvent.setRoutingContext(mockContext);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnCookieSetIsNull() {
+    Map<String, Object> handlerMap = new HashMap<>();
+    handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest);
+
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getHandlerContext()).thenReturn(handlerMap);
+    when(restClientRequest.getCookieMap()).thenReturn(null);
+
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+
+
+  @Test
+  public void serverFormattedElementOnNotFound() {
+    HashMap<String, Cookie> cookieSet = new HashMap<>();
+    CookieImpl cookie = new CookieImpl("anotherCookieName", COOKIE_VALUE);
+    cookieSet.put(cookie.getName(), cookie);
+    Mockito.when(mockContext.cookieCount()).thenReturn(1);
+    Mockito.when(mockContext.cookieMap()).thenReturn(cookieSet);
+    accessLogEvent.setRoutingContext(mockContext);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnNotFound() {
+    Map<String, Object> handlerMap = new HashMap<>();
+    handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest);
+
+    Map<String, String> cookieMap = new HashMap<>();
+    cookieMap.put("anotherCookieValue", COOKIE_VALUE);
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getHandlerContext()).thenReturn(handlerMap);
+    when(restClientRequest.getCookieMap()).thenReturn(cookieMap);
+
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/DatetimeConfigurableItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/DatetimeConfigurableItemTest.java
new file mode 100644
index 0000000..22bbffc
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/DatetimeConfigurableItemTest.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.apache.servicecomb.core.invocation.InvocationStageTrace;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class DatetimeConfigurableItemTest {
+
+  private static final long START_MILLISECOND = 1416863450581L;
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private Invocation invocation;
+
+  private InvocationStageTrace invocationStageTrace;
+
+  @Before
+  public void initStrBuilder() {
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    invocation = Mockito.mock(Invocation.class);
+    invocationStageTrace = Mockito.mock(InvocationStageTrace.class);
+
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace);
+    when(invocationStageTrace.getStartSend()).thenReturn(0L);
+    when(invocationStageTrace.getStart()).thenReturn(0L);
+    when(invocationStageTrace.getStartCurrentTime()).thenReturn(START_MILLISECOND);
+
+    accessLogEvent = new ServerAccessLogEvent();
+    accessLogEvent.setMilliStartTime(START_MILLISECOND);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void serverFormattedElement() {
+    DatetimeConfigurableItem element = new DatetimeConfigurableItem(
+        "EEE, yyyy MMM dd HH:mm:ss zzz|GMT-08|zh-CN");
+    element.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("星期一, 2014 十一月 24 13:10:50 GMT-08:00", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElement() {
+    DatetimeConfigurableItem element = new DatetimeConfigurableItem(
+        "EEE, yyyy MMM dd HH:mm:ss zzz|GMT-08|zh-CN");
+    element.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("星期一, 2014 十一月 24 13:10:50 GMT-08:00", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnNoPattern() {
+    DatetimeConfigurableItem element = new DatetimeConfigurableItem(
+        "|GMT+08|zh-CN");
+
+    element.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("星期二, 25 十一月 2014 05:10:50 GMT+08:00", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnNoPattern() {
+    DatetimeConfigurableItem element = new DatetimeConfigurableItem(
+        "|GMT+08|zh-CN");
+
+    element.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("星期二, 25 十一月 2014 05:10:50 GMT+08:00", strBuilder.toString());
+  }
+
+  @Test
+  public void getFormattedElementOnNoTimezone() {
+    DatetimeConfigurableItem element = new DatetimeConfigurableItem(
+        "yyyy/MM/dd zzz||zh-CN");
+    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd zzz", Locale.forLanguageTag("zh-CN"));
+    simpleDateFormat.setTimeZone(TimeZone.getDefault());
+
+    element.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnNoTimezone() {
+    DatetimeConfigurableItem element = new DatetimeConfigurableItem(
+        "yyyy/MM/dd zzz||zh-CN");
+    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd zzz", Locale.forLanguageTag("zh-CN"));
+    simpleDateFormat.setTimeZone(TimeZone.getDefault());
+
+    element.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnNoLocale() {
+    DatetimeConfigurableItem element = new DatetimeConfigurableItem(
+        "EEE, dd MMM yyyy HH:mm:ss zzz|GMT+08|");
+
+    element.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("Tue, 25 Nov 2014 05:10:50 GMT+08:00", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnNoLocale() {
+    DatetimeConfigurableItem element = new DatetimeConfigurableItem(
+        "EEE, dd MMM yyyy HH:mm:ss zzz|GMT+08|");
+
+    element.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("Tue, 25 Nov 2014 05:10:50 GMT+08:00", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnNoConfig() {
+    DatetimeConfigurableItem element = new DatetimeConfigurableItem(
+        "||");
+    SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DatetimeConfigurableItem.DEFAULT_DATETIME_PATTERN,
+        Locale.US);
+    simpleDateFormat.setTimeZone(TimeZone.getDefault());
+
+    element.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnNoConfig() {
+    DatetimeConfigurableItem element = new DatetimeConfigurableItem(
+        "||");
+    SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DatetimeConfigurableItem.DEFAULT_DATETIME_PATTERN,
+        Locale.US);
+    simpleDateFormat.setTimeZone(TimeZone.getDefault());
+
+    element.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString());
+  }
+
+  @Test
+  public void serverConstructorWithNoArg() {
+    DatetimeConfigurableItem element = new DatetimeConfigurableItem();
+    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+    simpleDateFormat.setTimeZone(TimeZone.getDefault());
+
+    element.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("EEE, dd MMM yyyy HH:mm:ss zzz", element.getPattern());
+    assertEquals(Locale.US, element.getLocale());
+    assertEquals(TimeZone.getDefault(), element.getTimezone());
+    assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString());
+  }
+
+  @Test
+  public void clientConstructorWithNoArg() {
+    DatetimeConfigurableItem element = new DatetimeConfigurableItem();
+    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+    simpleDateFormat.setTimeZone(TimeZone.getDefault());
+
+    element.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("EEE, dd MMM yyyy HH:mm:ss zzz", element.getPattern());
+    assertEquals(Locale.US, element.getLocale());
+    assertEquals(TimeZone.getDefault(), element.getTimezone());
+    assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString());
+  }
+
+  @Test
+  public void serverConstructorWithNoSeparator() {
+    DatetimeConfigurableItem element = new DatetimeConfigurableItem("yyyy/MM/dd HH:mm:ss zzz");
+    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss zzz", Locale.US);
+    simpleDateFormat.setTimeZone(TimeZone.getDefault());
+
+    element.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("yyyy/MM/dd HH:mm:ss zzz", element.getPattern());
+    assertEquals(Locale.US, element.getLocale());
+    assertEquals(TimeZone.getDefault(), element.getTimezone());
+    assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString());
+  }
+
+  @Test
+  public void clientConstructorWithNoSeparator() {
+    DatetimeConfigurableItem element = new DatetimeConfigurableItem("yyyy/MM/dd HH:mm:ss zzz");
+    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss zzz", Locale.US);
+    simpleDateFormat.setTimeZone(TimeZone.getDefault());
+
+    element.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("yyyy/MM/dd HH:mm:ss zzz", element.getPattern());
+    assertEquals(Locale.US, element.getLocale());
+    assertEquals(TimeZone.getDefault(), element.getTimezone());
+    assertEquals(simpleDateFormat.format(START_MILLISECOND), strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/DurationMillisecondItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/DurationMillisecondItemTest.java
new file mode 100644
index 0000000..231aae8
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/DurationMillisecondItemTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.apache.servicecomb.core.invocation.InvocationStageTrace;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class DurationMillisecondItemTest {
+
+  public static final DurationMillisecondItem ELEMENT = new DurationMillisecondItem();
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private Invocation invocation;
+
+  private InvocationStageTrace invocationStageTrace;
+
+  @Before
+  public void initStrBuilder() {
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    invocation = Mockito.mock(Invocation.class);
+    invocationStageTrace = Mockito.mock(InvocationStageTrace.class);
+
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace);
+    when(invocationStageTrace.getStartSend()).thenReturn(0L);
+    when(invocationStageTrace.getFinish()).thenReturn(1000_000L);
+
+    accessLogEvent = new ServerAccessLogEvent();
+    accessLogEvent.setMilliStartTime(1L);
+    accessLogEvent.setMilliEndTime(2L);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void testAppendFormattedElement() {
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("1", strBuilder.toString());
+
+    strBuilder = new StringBuilder();
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("1", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/DurationSecondItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/DurationSecondItemTest.java
new file mode 100644
index 0000000..2e983eb
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/DurationSecondItemTest.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.apache.servicecomb.core.invocation.InvocationStageTrace;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class DurationSecondItemTest {
+
+  public static final DurationSecondItem ELEMENT = new DurationSecondItem();
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private Invocation invocation;
+
+  private InvocationStageTrace invocationStageTrace;
+
+  @Before
+  public void initStrBuilder() {
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    invocation = Mockito.mock(Invocation.class);
+    invocationStageTrace = Mockito.mock(InvocationStageTrace.class);
+
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace);
+    when(invocationStageTrace.getStartSend()).thenReturn(1000_000L);
+
+    accessLogEvent = new ServerAccessLogEvent();
+    accessLogEvent.setMilliStartTime(1L);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void serverFormattedElementOn999ms() {
+    accessLogEvent.setMilliEndTime(1000L);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("0", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOn999ms() {
+    when(invocationStageTrace.getFinish()).thenReturn(1000_000_000L);
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("0", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOn1000ms() {
+    accessLogEvent.setMilliEndTime(1001L);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("1", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOn1000ms() {
+    when(invocationStageTrace.getFinish()).thenReturn(1001_000_000L);
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("1", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOn1001ms() {
+    accessLogEvent.setMilliEndTime(1002L);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("1", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOn1001ms() {
+    when(invocationStageTrace.getFinish()).thenReturn(1002_000_000L);
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("1", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/FirstLineOfRequestItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/FirstLineOfRequestItemTest.java
new file mode 100644
index 0000000..bf971ac
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/FirstLineOfRequestItemTest.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.servicecomb.common.rest.RestConst;
+import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl;
+import org.apache.servicecomb.core.Endpoint;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.apache.servicecomb.foundation.common.net.URIEndpointObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.core.http.HttpClientRequest;
+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 FirstLineOfRequestItemTest {
+
+  public static final FirstLineOfRequestItem ELEMENT = new FirstLineOfRequestItem();
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext mockContext;
+
+  private Invocation invocation;
+
+  private RestClientRequestImpl restClientRequest;
+
+  private HttpClientRequest clientRequest;
+
+  private Endpoint endpoint;
+
+  private URIEndpointObject urlEndpoint;
+
+  @Before
+  public void initStrBuilder() {
+    mockContext = Mockito.mock(RoutingContext.class);
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    invocation = Mockito.mock(Invocation.class);
+    restClientRequest = Mockito.mock(RestClientRequestImpl.class);
+    clientRequest = Mockito.mock(HttpClientRequest.class);
+    endpoint = Mockito.mock(Endpoint.class);
+    urlEndpoint = Mockito.mock(URIEndpointObject.class);
+    Map<String, Object> handlerMap = new HashMap<>();
+    handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest);
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getHandlerContext()).thenReturn(handlerMap);
+    when(restClientRequest.getRequest()).thenReturn(clientRequest);
+    when(invocation.getEndpoint()).thenReturn(endpoint);
+    when(endpoint.getAddress()).thenReturn(urlEndpoint);
+    accessLogEvent = new ServerAccessLogEvent();
+    accessLogEvent.setRoutingContext(mockContext);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void serverFormattedElement() {
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    String uri = "/test/uri";
+
+    when(mockContext.request()).thenReturn(request);
+    when(request.method()).thenReturn(HttpMethod.DELETE);
+    when(request.path()).thenReturn(uri);
+    when(request.version()).thenReturn(HttpVersion.HTTP_1_1);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("\"DELETE " + uri + " HTTP/1.1\"", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElement() {
+    String uri = "/test/uri";
+    when(urlEndpoint.isSslEnabled()).thenReturn(false);
+    when(clientRequest.method()).thenReturn(HttpMethod.DELETE);
+    when(clientRequest.path()).thenReturn(uri);
+
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("\"DELETE " + uri + " HTTP\"", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/HttpMethodItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/HttpMethodItemTest.java
new file mode 100644
index 0000000..012a965
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/HttpMethodItemTest.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.servicecomb.common.rest.RestConst;
+import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl;
+import org.apache.servicecomb.core.Endpoint;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.apache.servicecomb.foundation.common.net.URIEndpointObject;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.core.http.HttpClientRequest;
+import io.vertx.core.http.HttpMethod;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.ext.web.RoutingContext;
+
+public class HttpMethodItemTest {
+  private static final HttpMethodItem ITEM = new HttpMethodItem();
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext routingContext;
+
+  private Invocation invocation;
+
+  private RestClientRequestImpl restClientRequest;
+
+  private HttpClientRequest clientRequest;
+
+  private Endpoint endpoint;
+
+  private URIEndpointObject urlEndpoint;
+
+  @Before
+  public void initStrBuilder() {
+    routingContext = Mockito.mock(RoutingContext.class);
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    invocation = Mockito.mock(Invocation.class);
+    restClientRequest = Mockito.mock(RestClientRequestImpl.class);
+    clientRequest = Mockito.mock(HttpClientRequest.class);
+    endpoint = Mockito.mock(Endpoint.class);
+    urlEndpoint = Mockito.mock(URIEndpointObject.class);
+    Map<String, Object> handlerMap = new HashMap<>();
+    handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest);
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getHandlerContext()).thenReturn(handlerMap);
+    when(invocation.getEndpoint()).thenReturn(endpoint);
+    when(endpoint.getAddress()).thenReturn(urlEndpoint);
+    accessLogEvent = new ServerAccessLogEvent();
+    accessLogEvent.setRoutingContext(routingContext);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void serverFormattedElement() {
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    Mockito.when(routingContext.request()).thenReturn(request);
+    Mockito.when(request.method()).thenReturn(HttpMethod.DELETE);
+    accessLogEvent.setRoutingContext(routingContext);
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertEquals("DELETE", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElement() {
+    when(restClientRequest.getRequest()).thenReturn(clientRequest);
+    when(clientRequest.method()).thenReturn(HttpMethod.DELETE);
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals("DELETE", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnRequestIsNull() {
+    accessLogEvent.setRoutingContext(routingContext);
+    Mockito.when(routingContext.request()).thenReturn(null);
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnRequestIsNull() {
+    when(restClientRequest.getRequest()).thenReturn(null);
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+
+
+  @Test
+  public void serverFormattedElementOnMethodIsNull() {
+    HttpServerRequest request = Mockito.mock(HttpServerRequest.class);
+    accessLogEvent.setRoutingContext(routingContext);
+
+    Mockito.when(routingContext.request()).thenReturn(request);
+    Mockito.when(request.method()).thenReturn(null);
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnMethodIsNull() {
+    when(clientRequest.method()).thenReturn(null);
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/HttpStatusItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/HttpStatusItemTest.java
new file mode 100644
index 0000000..6a43b88
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/HttpStatusItemTest.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.ext.web.RoutingContext;
+
+public class HttpStatusItemTest {
+
+  private static final HttpStatusItem STATUS_ELEMENT = new HttpStatusItem();
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext routingContext;
+
+  private Response response;
+
+  private HttpServerResponse serverResponse;
+
+  @Before
+  public void initStrBuilder() {
+    routingContext = Mockito.mock(RoutingContext.class);
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    response = Mockito.mock(Response.class);
+    serverResponse = Mockito.mock(HttpServerResponse.class);
+
+    accessLogEvent = new ServerAccessLogEvent();
+    accessLogEvent.setRoutingContext(routingContext);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void serverFormattedElement() {
+    int statusCode = 200;
+    when(routingContext.response()).thenReturn(serverResponse);
+    when(serverResponse.getStatusCode()).thenReturn(statusCode);
+
+    STATUS_ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("200", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElement() {
+    int statusCode = 200;
+    when(finishEvent.getResponse()).thenReturn(response);
+    when(response.getStatusCode()).thenReturn(statusCode);
+
+    STATUS_ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("200", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnResponseIsNull() {
+    Mockito.when(routingContext.response()).thenReturn(null);
+    STATUS_ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+
+    Mockito.when(routingContext.response()).thenReturn(serverResponse);
+    Mockito.when(serverResponse.closed()).thenReturn(true);
+    Mockito.when(serverResponse.ended()).thenReturn(false);
+
+    strBuilder = new StringBuilder();
+    STATUS_ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnResponseIsNull() {
+    when(finishEvent.getResponse()).thenReturn(null);
+    STATUS_ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/InvocationContextItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/InvocationContextItemTest.java
new file mode 100644
index 0000000..48952d4
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/InvocationContextItemTest.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.servicecomb.common.rest.RestConst;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.ext.web.RoutingContext;
+
+public class InvocationContextItemTest {
+
+  public static final String INVOCATION_CONTEXT_KEY = "testKey";
+
+  public static final String INVOCATION_CONTEXT_VALUE = "testValue";
+
+  private static InvocationContextItem ITEM = new InvocationContextItem(INVOCATION_CONTEXT_KEY);
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext routingContext;
+
+  private Invocation invocation;
+
+  @Before
+  public void initStrBuilder() {
+    accessLogEvent = new ServerAccessLogEvent();
+    routingContext = Mockito.mock(RoutingContext.class);
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    invocation = Mockito.mock(Invocation.class);
+
+    accessLogEvent.setRoutingContext(routingContext);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void serverGetFormattedItem() {
+    Map<String, Object> routingContextData = new HashMap<>();
+    when(routingContext.data()).thenReturn(routingContextData);
+    routingContextData.put(RestConst.REST_INVOCATION_CONTEXT, invocation);
+    when(invocation.getContext(INVOCATION_CONTEXT_KEY)).thenReturn(INVOCATION_CONTEXT_VALUE);
+
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), Matchers.is(INVOCATION_CONTEXT_VALUE));
+  }
+
+  @Test
+  public void clientGetFormattedItem() {
+    Map<String, String> context = new HashMap<>();
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getContext()).thenReturn(context);
+    context.put(INVOCATION_CONTEXT_KEY, INVOCATION_CONTEXT_VALUE);
+
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), Matchers.is(INVOCATION_CONTEXT_VALUE));
+  }
+
+  @Test
+  public void serverGetFormattedItemOnInvocationContextValueNotFound() {
+    Map<String, Object> routingContextData = new HashMap<>();
+    Invocation invocation = Mockito.mock(Invocation.class);
+    when(routingContext.data()).thenReturn(routingContextData);
+    routingContextData.put(RestConst.REST_INVOCATION_CONTEXT, invocation);
+    when(invocation.getContext(INVOCATION_CONTEXT_KEY)).thenReturn(null);
+
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextItem.NOT_FOUND));
+  }
+
+  @Test
+  public void clientGetFormattedItemOnInvocationContextValueNotFound() {
+    Map<String, String> context = new HashMap<>();
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getContext()).thenReturn(context);
+    context.put(INVOCATION_CONTEXT_KEY, null);
+
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextItem.NOT_FOUND));
+  }
+
+  @Test
+  public void serverGetFormattedItemOnInvocationNotFound() {
+    Map<String, Object> routingContextData = new HashMap<>();
+    when(routingContext.data()).thenReturn(routingContextData);
+
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextItem.NOT_FOUND));
+  }
+
+  @Test
+  public void clientGetFormattedItemOnInvocationContextNotFound() {
+    Map<String, String> context = new HashMap<>();
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getContext()).thenReturn(context);
+
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextItem.NOT_FOUND));
+  }
+
+  @Test
+  public void testGetFormattedItemOnRoutingContextDataNotFound() {
+    when(routingContext.data()).thenReturn(null);
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextItem.NOT_FOUND));
+  }
+
+  @Test
+  public void clientGetFormattedItemOnRoutingContextDataNotFound() {
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getContext()).thenReturn(null);
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextItem.NOT_FOUND));
+  }
+
+  @Test
+  public void clientGetFormattedItemOnInvocationNotFound() {
+    when(finishEvent.getInvocation()).thenReturn(null);
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), Matchers.is(InvocationContextItem.NOT_FOUND));
+  }
+}
\ No newline at end of file
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/LocalHostItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/LocalHostItemTest.java
new file mode 100644
index 0000000..18d5471
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/LocalHostItemTest.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.net.SocketAddress;
+import io.vertx.ext.web.RoutingContext;
+
+public class LocalHostItemTest {
+  public static final LocalHostItem ELEMENT = new LocalHostItem();
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext routingContext;
+
+  private HttpServerRequest serverRequest;
+
+  private SocketAddress socketAddress;
+
+  @Before
+  public void initStrBuilder() {
+    accessLogEvent = new ServerAccessLogEvent();
+    routingContext = Mockito.mock(RoutingContext.class);
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    serverRequest = Mockito.mock(HttpServerRequest.class);
+    socketAddress = Mockito.mock(SocketAddress.class);
+
+    accessLogEvent.setRoutingContext(routingContext);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void clientFormattedItem() {
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedItem() {
+    String localAddress = "192.168.0.1";
+    accessLogEvent.setLocalAddress(localAddress);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertEquals(localAddress, strBuilder.toString());
+  }
+
+  @Test
+  public void getLocalAddress() {
+    String localHost = "testHost";
+    Mockito.when(routingContext.request()).thenReturn(serverRequest);
+    Mockito.when(serverRequest.localAddress()).thenReturn(socketAddress);
+    Mockito.when(socketAddress.host()).thenReturn(localHost);
+
+    String result = LocalHostItem.getLocalAddress(routingContext);
+    assertEquals(localHost, result);
+  }
+
+  @Test
+  public void getLocalAddressOnRequestIsNull() {
+    Mockito.when(routingContext.request()).thenReturn(null);
+    String result = LocalHostItem.getLocalAddress(routingContext);
+    assertEquals("-", result);
+  }
+
+  @Test
+  public void getLocalAddressOnLocalAddressIsNull() {
+    Mockito.when(routingContext.request()).thenReturn(serverRequest);
+    Mockito.when(serverRequest.localAddress()).thenReturn(null);
+    String result = LocalHostItem.getLocalAddress(routingContext);
+    assertEquals("-", result);
+  }
+
+  @Test
+  public void getLocalAddressOnHostIsNull() {
+    Mockito.when(routingContext.request()).thenReturn(serverRequest);
+    Mockito.when(serverRequest.localAddress()).thenReturn(socketAddress);
+    Mockito.when(socketAddress.host()).thenReturn(null);
+
+    String result = LocalHostItem.getLocalAddress(routingContext);
+    assertEquals("-", result);
+  }
+
+  @Test
+  public void getLocalAddressIsEmpty() {
+    String localHost = "";
+    Mockito.when(routingContext.request()).thenReturn(serverRequest);
+    Mockito.when(serverRequest.localAddress()).thenReturn(socketAddress);
+    Mockito.when(socketAddress.host()).thenReturn(localHost);
+
+    String result = LocalHostItem.getLocalAddress(routingContext);
+    assertEquals("-", result);
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/LocalPortItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/LocalPortItemTest.java
new file mode 100644
index 0000000..e27359b
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/LocalPortItemTest.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.net.SocketAddress;
+import io.vertx.ext.web.RoutingContext;
+
+public class LocalPortItemTest {
+  public static final LocalPortItem ELEMENT = new LocalPortItem();
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext routingContext;
+
+  private HttpServerRequest serverRequest;
+
+  private SocketAddress socketAddress;
+
+  @Before
+  public void initStrBuilder() {
+    accessLogEvent = new ServerAccessLogEvent();
+    routingContext = Mockito.mock(RoutingContext.class);
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    serverRequest = Mockito.mock(HttpServerRequest.class);
+    socketAddress = Mockito.mock(SocketAddress.class);
+
+    accessLogEvent.setRoutingContext(routingContext);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void serverFormattedElement() {
+    Mockito.when(routingContext.request()).thenReturn(serverRequest);
+    Mockito.when(serverRequest.localAddress()).thenReturn(socketAddress);
+    Mockito.when(socketAddress.port()).thenReturn(8080);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("8080", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElement() {
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void getFormattedElementOnRequestIsNull() {
+    Mockito.when(routingContext.request()).thenReturn(null);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void getFormattedElementOnLocalAddressIsNull() {
+    Mockito.when(routingContext.request()).thenReturn(serverRequest);
+    Mockito.when(serverRequest.localAddress()).thenReturn(null);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/TraceIdItem.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/PlainTextItemTest.java
similarity index 52%
copy from foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/TraceIdItem.java
copy to foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/PlainTextItemTest.java
index 5c35e92..2fcb201 100644
--- a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/TraceIdItem.java
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/PlainTextItemTest.java
@@ -17,32 +17,39 @@
 
 package org.apache.servicecomb.foundation.log.core.element.impl;
 
-import org.apache.servicecomb.core.Const;
+import static org.junit.Assert.assertEquals;
+
 import org.apache.servicecomb.core.event.InvocationFinishEvent;
 import org.apache.servicecomb.core.event.ServerAccessLogEvent;
-import org.springframework.util.StringUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
 
-public class TraceIdItem extends InvocationContextItem {
+public class PlainTextItemTest {
+  private InvocationFinishEvent finishEvent;
 
-  public static final String TRACE_ID = Const.TRACE_ID_NAME;
+  private ServerAccessLogEvent accessLogEvent;
 
-  public TraceIdItem() {
-    super(TRACE_ID);
-  }
+  private StringBuilder strBuilder;
 
-  @Override
-  public void appendFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) {
-    String traceId = getValueFromInvocationContext(accessLogEvent);
-    if (StringUtils.isEmpty(traceId)) {
-      traceId = accessLogEvent.getRoutingContext().request().getHeader(TRACE_ID);
-    }
+  @Before
+  public void initStrBuilder() {
+    accessLogEvent = new ServerAccessLogEvent();
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    strBuilder = new StringBuilder();
+  }
 
-    builder.append(StringUtils.isEmpty(traceId) ? InvocationContextItem.NOT_FOUND : traceId);
+  @Test
+  public void serverFormattedElement() {
+    PlainTextItem element = new PlainTextItem("contentTest");
+    element.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("contentTest", strBuilder.toString());
   }
 
-  @Override
-  public void appendFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) {
-    String traceId = finishEvent.getInvocation().getContext().get(TRACE_ID);
-    builder.append(StringUtils.isEmpty(traceId) ? InvocationContextItem.NOT_FOUND : traceId);
+  @Test
+  public void clientFormattedElement() {
+    PlainTextItem element = new PlainTextItem("contentTest");
+    element.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("contentTest", strBuilder.toString());
   }
 }
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/QueryStringItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/QueryStringItemTest.java
new file mode 100644
index 0000000..5aa8f1f
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/QueryStringItemTest.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.servicecomb.common.rest.RestConst;
+import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.core.http.HttpClientRequest;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.ext.web.RoutingContext;
+
+public class QueryStringItemTest {
+  private static final QueryStringItem ITEM = new QueryStringItem();
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext routingContext;
+
+  private Invocation invocation;
+
+  private RestClientRequestImpl restClientRequest;
+
+  private HttpServerRequest serverRequest;
+
+  private HttpClientRequest clientRequest;
+
+  @Before
+  public void initStrBuilder() {
+    routingContext = Mockito.mock(RoutingContext.class);
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    invocation = Mockito.mock(Invocation.class);
+    serverRequest = Mockito.mock(HttpServerRequest.class);
+    restClientRequest = Mockito.mock(RestClientRequestImpl.class);
+    clientRequest = Mockito.mock(HttpClientRequest.class);
+
+    accessLogEvent = new ServerAccessLogEvent();
+    accessLogEvent.setRoutingContext(routingContext);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void serverFormattedElement() {
+    String query = "?status=up";
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.query()).thenReturn(query);
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals(query, strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElement() {
+    String query = "?status=up";
+    Map<String, Object> handlerContext = new HashMap<>();
+    handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest);
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getHandlerContext()).thenReturn(handlerContext);
+    when(restClientRequest.getRequest()).thenReturn(clientRequest);
+    when(clientRequest.query()).thenReturn(query);
+
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals(query, strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnRequestIsNull() {
+    when(routingContext.request()).thenReturn(null);
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnRequestIsNull() {
+    Map<String, Object> handlerContext = new HashMap<>();
+    handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest);
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getHandlerContext()).thenReturn(handlerContext);
+    when(restClientRequest.getRequest()).thenReturn(null);
+
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnQueryIsNull() {
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.query()).thenReturn(null);
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnQueryIsNull() {
+    Map<String, Object> handlerContext = new HashMap<>();
+    handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest);
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getHandlerContext()).thenReturn(handlerContext);
+    when(restClientRequest.getRequest()).thenReturn(clientRequest);
+    when(clientRequest.query()).thenReturn(null);
+
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnQueryIsEmpty() {
+    String query = "";
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.query()).thenReturn(query);
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnQueryIsEmpty() {
+    String query = "";
+    Map<String, Object> handlerContext = new HashMap<>();
+    handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest);
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getHandlerContext()).thenReturn(handlerContext);
+    when(restClientRequest.getRequest()).thenReturn(clientRequest);
+    when(clientRequest.query()).thenReturn(query);
+
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/RemoteHostItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/RemoteHostItemTest.java
new file mode 100644
index 0000000..24967d9
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/RemoteHostItemTest.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.apache.servicecomb.core.Endpoint;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.apache.servicecomb.foundation.common.net.URIEndpointObject;
+import org.junit.Before;
+import org.junit.Test;
+
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.net.SocketAddress;
+import io.vertx.ext.web.RoutingContext;
+
+public class RemoteHostItemTest {
+  public static final RemoteHostItem ELEMENT = new RemoteHostItem();
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext routingContext;
+
+  private Invocation invocation;
+
+  private HttpServerRequest serverRequest;
+
+  private Endpoint endpoint;
+
+  private URIEndpointObject uriEndpointObject;
+
+  private SocketAddress socketAddress;
+
+  @Before
+  public void initStrBuilder() {
+    routingContext = mock(RoutingContext.class);
+    finishEvent = mock(InvocationFinishEvent.class);
+    invocation = mock(Invocation.class);
+    serverRequest = mock(HttpServerRequest.class);
+    endpoint = mock(Endpoint.class);
+    uriEndpointObject = mock(URIEndpointObject.class);
+    socketAddress = mock(SocketAddress.class);
+    accessLogEvent = new ServerAccessLogEvent();
+    accessLogEvent.setRoutingContext(routingContext);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void serverFormattedElement() {
+    String remoteHost = "remoteHost";
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.remoteAddress()).thenReturn(socketAddress);
+    when(socketAddress.host()).thenReturn(remoteHost);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals(remoteHost, strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElement() {
+    String remoteHost = "remoteHost";
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getEndpoint()).thenReturn(endpoint);
+    when(endpoint.getAddress()).thenReturn(uriEndpointObject);
+    when(uriEndpointObject.getHostOrIp()).thenReturn(remoteHost);
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals(remoteHost, strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnRequestIsNull() {
+    when(routingContext.request()).thenReturn(null);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnRequestIsNull() {
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getEndpoint()).thenReturn(null);
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnRemoteAddressIsNull() {
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.remoteAddress()).thenReturn(null);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnRemoteAddressIsNull() {
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getEndpoint()).thenReturn(endpoint);
+    when(endpoint.getAddress()).thenReturn(null);
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnHostIsNull() {
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.remoteAddress()).thenReturn(socketAddress);
+    when(socketAddress.host()).thenReturn(null);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnHostIsNull() {
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getEndpoint()).thenReturn(endpoint);
+    when(endpoint.getAddress()).thenReturn(uriEndpointObject);
+    when(uriEndpointObject.getHostOrIp()).thenReturn(null);
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnHostIsEmpty() {
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.remoteAddress()).thenReturn(socketAddress);
+    when(socketAddress.host()).thenReturn("");
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnHostIsEmpty() {
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getEndpoint()).thenReturn(endpoint);
+    when(endpoint.getAddress()).thenReturn(uriEndpointObject);
+    when(uriEndpointObject.getHostOrIp()).thenReturn("");
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/RequestHeaderItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/RequestHeaderItemTest.java
new file mode 100644
index 0000000..3d84b9b
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/RequestHeaderItemTest.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.servicecomb.common.rest.RestConst;
+import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.core.MultiMap;
+import io.vertx.core.http.HttpClientRequest;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.http.impl.headers.VertxHttpHeaders;
+import io.vertx.ext.web.RoutingContext;
+
+public class RequestHeaderItemTest {
+  private static final String VAR_NAME = "varName";
+
+  private static final RequestHeaderItem ELEMENT = new RequestHeaderItem(VAR_NAME);
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext routingContext;
+
+  private Invocation invocation;
+
+  private RestClientRequestImpl restClientRequest;
+
+  private HttpServerRequest serverRequest;
+
+  private HttpClientRequest clientRequest;
+
+  private MultiMap headers;
+
+  @Before
+  public void initStrBuilder() {
+    routingContext = Mockito.mock(RoutingContext.class);
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    invocation = Mockito.mock(Invocation.class);
+    serverRequest = Mockito.mock(HttpServerRequest.class);
+    restClientRequest = Mockito.mock(RestClientRequestImpl.class);
+    clientRequest = Mockito.mock(HttpClientRequest.class);
+    headers = Mockito.mock(MultiMap.class);
+
+    accessLogEvent = new ServerAccessLogEvent();
+    accessLogEvent.setRoutingContext(routingContext);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void serverFormattedElement() {
+    VertxHttpHeaders headers = new VertxHttpHeaders();
+    String testValue = "testValue";
+    headers.add(VAR_NAME, testValue);
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.headers()).thenReturn(headers);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals(testValue, strBuilder.toString());
+    assertEquals(ELEMENT.getVarName(), VAR_NAME);
+  }
+
+  @Test
+  public void clientFormattedElement() {
+    Map<String, Object> handlerContext = new HashMap<>();
+    String testValue = "testValue";
+
+    handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest);
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getHandlerContext()).thenReturn(handlerContext);
+    when(restClientRequest.getRequest()).thenReturn(clientRequest);
+    when(clientRequest.headers()).thenReturn(headers);
+    when(headers.get(VAR_NAME)).thenReturn(testValue);
+
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals(testValue, strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementIfHeaderIsNull() {
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.headers()).thenReturn(null);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementIfHeaderIsNull() {
+    Map<String, Object> handlerContext = new HashMap<>();
+    handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest);
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getHandlerContext()).thenReturn(handlerContext);
+    when(restClientRequest.getRequest()).thenReturn(clientRequest);
+    when(clientRequest.headers()).thenReturn(null);
+
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementIfNotFound() {
+    VertxHttpHeaders headers = new VertxHttpHeaders();
+    String testValue = "testValue";
+    headers.add("anotherKey", testValue);
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.headers()).thenReturn(headers);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/RequestProtocolItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/RequestProtocolItemTest.java
new file mode 100644
index 0000000..d85151b
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/RequestProtocolItemTest.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import org.apache.servicecomb.core.Endpoint;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.apache.servicecomb.foundation.common.net.URIEndpointObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.http.HttpVersion;
+import io.vertx.ext.web.RoutingContext;
+
+public class RequestProtocolItemTest {
+  private static final RequestProtocolItem ITEM = new RequestProtocolItem();
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext routingContext;
+
+  private Invocation invocation;
+
+  private HttpServerRequest serverRequest;
+
+  private Endpoint endpoint;
+
+  private URIEndpointObject urlEndpoint;
+
+  @Before
+  public void initStrBuilder() {
+    routingContext = Mockito.mock(RoutingContext.class);
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    invocation = Mockito.mock(Invocation.class);
+    serverRequest = Mockito.mock(HttpServerRequest.class);
+    urlEndpoint = Mockito.mock(URIEndpointObject.class);
+    endpoint = Mockito.mock(Endpoint.class);
+
+    accessLogEvent = new ServerAccessLogEvent();
+    accessLogEvent.setRoutingContext(routingContext);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void serverFormattedElement() {
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.version()).thenReturn(HttpVersion.HTTP_1_1);
+
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("HTTP/1.1", strBuilder.toString());
+
+    strBuilder = new StringBuilder();
+    when(serverRequest.version()).thenReturn(HttpVersion.HTTP_1_0);
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("HTTP/1.0", strBuilder.toString());
+
+    strBuilder = new StringBuilder();
+    when(serverRequest.version()).thenReturn(HttpVersion.HTTP_2);
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("HTTP/2.0", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnRequestIsNull() {
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getEndpoint()).thenReturn(endpoint);
+    when(endpoint.getAddress()).thenReturn(urlEndpoint);
+    when(urlEndpoint.isSslEnabled()).thenReturn(true);
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("HTTPS", strBuilder.toString());
+
+    strBuilder = new StringBuilder();
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getEndpoint()).thenReturn(endpoint);
+    when(endpoint.getAddress()).thenReturn(urlEndpoint);
+    when(urlEndpoint.isSslEnabled()).thenReturn(false);
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("HTTP", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnVersionIsNull() {
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.version()).thenReturn(null);
+
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnVersionIsNull() {
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getEndpoint()).thenReturn(endpoint);
+    when(endpoint.getAddress()).thenReturn(null);
+
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/ResponseHeaderItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/ResponseHeaderItemTest.java
new file mode 100644
index 0000000..dad7e31
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/ResponseHeaderItemTest.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.apache.servicecomb.swagger.invocation.response.Headers;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.core.http.impl.headers.VertxHttpHeaders;
+import io.vertx.ext.web.RoutingContext;
+
+public class ResponseHeaderItemTest {
+
+  private static final String VAR_NAME = "varName";
+
+  private static final ResponseHeaderItem ELEMENT = new ResponseHeaderItem(VAR_NAME);
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext routingContext;
+
+  private HttpServerResponse serverResponse;
+
+  private Response response;
+
+  @Before
+  public void initStrBuilder() {
+    routingContext = Mockito.mock(RoutingContext.class);
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    serverResponse = Mockito.mock(HttpServerResponse.class);
+    response = Mockito.mock(Response.class);
+
+    accessLogEvent = new ServerAccessLogEvent();
+    accessLogEvent.setRoutingContext(routingContext);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void serverFormattedElement() {
+    VertxHttpHeaders headers = new VertxHttpHeaders();
+    String headerValue = "headerValue";
+    headers.add(VAR_NAME, headerValue);
+    when(routingContext.response()).thenReturn(serverResponse);
+    when(serverResponse.headers()).thenReturn(headers);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals(headerValue, strBuilder.toString());
+    assertEquals(ELEMENT.getVarName(), VAR_NAME);
+  }
+
+  @Test
+  public void clientFormattedElement() {
+    String headerValue = "headerValue";
+    Headers headers = new Headers();
+    Map<String, List<Object>> headerMap = new HashMap<>();
+    headerMap.put(VAR_NAME, Arrays.asList(headerValue));
+
+    headers.setHeaderMap(headerMap);
+    when(finishEvent.getResponse()).thenReturn(response);
+    when(response.getHeaders()).thenReturn(headers);
+
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals(headerValue, strBuilder.toString());
+    assertEquals(ELEMENT.getVarName(), VAR_NAME);
+  }
+
+  @Test
+  public void serverFormattedElementOnHeadersIsNull() {
+    when(routingContext.response()).thenReturn(serverResponse);
+    when(serverResponse.headers()).thenReturn(null);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnHeadersIsNull() {
+    when(finishEvent.getResponse()).thenReturn(response);
+    when(response.getHeaders()).thenReturn(null);
+
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnResponseIsNull() {
+    when(routingContext.response()).thenReturn(null);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnResponseIsNull() {
+    when(finishEvent.getResponse()).thenReturn(null);
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnNotFound() {
+    VertxHttpHeaders headers = new VertxHttpHeaders();
+    String headerValue = "headerValue";
+    headers.add("anotherHeader", headerValue);
+    when(routingContext.response()).thenReturn(serverResponse);
+    when(serverResponse.headers()).thenReturn(headers);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnNotFound() {
+    String headerValue = "headerValue";
+    Headers headers = new Headers();
+    Map<String, List<Object>> headerMap = new HashMap<>();
+    headerMap.put("anotherHeader", Arrays.asList(headerValue));
+    headers.setHeaderMap(headerMap);
+    when(finishEvent.getResponse()).thenReturn(response);
+    when(response.getHeaders()).thenReturn(headers);
+
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/ResponseSizeItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/ResponseSizeItemTest.java
new file mode 100644
index 0000000..0db4f27
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/ResponseSizeItemTest.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.ext.web.RoutingContext;
+
+public class ResponseSizeItemTest {
+  private static final ResponseSizeItem ELEMENT = new ResponseSizeItem("0");
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext routingContext;
+
+  private HttpServerResponse serverResponse;
+
+  @Before
+  public void initStrBuilder() {
+    routingContext = Mockito.mock(RoutingContext.class);
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    serverResponse = Mockito.mock(HttpServerResponse.class);
+
+    accessLogEvent = new ServerAccessLogEvent();
+    accessLogEvent.setRoutingContext(routingContext);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void serverFormattedElement() {
+    long bytesWritten = 16L;
+    when(routingContext.response()).thenReturn(serverResponse);
+    when(serverResponse.bytesWritten()).thenReturn(bytesWritten);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals(String.valueOf(bytesWritten), strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElement() {
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("0", strBuilder.toString());
+  }
+
+  @Test
+  public void getFormattedElementOnResponseIsNull() {
+    when(routingContext.response()).thenReturn(null);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("0", strBuilder.toString());
+  }
+
+  @Test
+  public void getFormattedElementOnBytesWrittenIsZero() {
+    long bytesWritten = 0L;
+    when(routingContext.response()).thenReturn(serverResponse);
+    when(serverResponse.bytesWritten()).thenReturn(bytesWritten);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("0", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/TraceIdItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/TraceIdItemTest.java
new file mode 100644
index 0000000..779561d
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/TraceIdItemTest.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.hamcrest.core.Is.is;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.servicecomb.common.rest.RestConst;
+import org.apache.servicecomb.core.Const;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.ext.web.RoutingContext;
+
+public class TraceIdItemTest {
+  private static final TraceIdItem ELEMENT = new TraceIdItem();
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext routingContext;
+
+  private HttpServerRequest serverRequest;
+
+  private Invocation invocation;
+
+  private Map<String, String> clientContext = new HashMap<>();
+
+  @Before
+  public void initStrBuilder() {
+    accessLogEvent = new ServerAccessLogEvent();
+    routingContext = Mockito.mock(RoutingContext.class);
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    invocation = Mockito.mock(Invocation.class);
+    serverRequest = Mockito.mock(HttpServerRequest.class);
+
+    accessLogEvent.setRoutingContext(routingContext);
+    strBuilder = new StringBuilder();
+    clientContext.clear();
+  }
+
+  @Test
+  public void serverGetFormattedElementFromInvocationContext() {
+    Map<String, Object> data = new HashMap<>();
+    String traceIdTest = "traceIdTest";
+    when(invocation.getContext(Const.TRACE_ID_NAME)).thenReturn(traceIdTest);
+    when(routingContext.data()).thenReturn(data);
+    data.put(RestConst.REST_INVOCATION_CONTEXT, invocation);
+
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), is(traceIdTest));
+  }
+
+  @Test
+  public void clientGetFormattedElementFromInvocationContext() {
+    String traceIdTest = "traceIdTest";
+    clientContext.put(Const.TRACE_ID_NAME, traceIdTest);
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getContext()).thenReturn(clientContext);
+
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), is(traceIdTest));
+  }
+
+  @Test
+  public void serverGetFormattedElementFromRequestHeader() {
+    Map<String, Object> data = new HashMap<>();
+    String traceIdTest = "traceIdTest";
+    when(invocation.getContext(Const.TRACE_ID_NAME)).thenReturn(null);
+    when(routingContext.data()).thenReturn(data);
+    data.put(RestConst.REST_INVOCATION_CONTEXT, invocation);
+
+    when(serverRequest.getHeader(Const.TRACE_ID_NAME)).thenReturn(traceIdTest);
+    when(routingContext.request()).thenReturn(serverRequest);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), is(traceIdTest));
+  }
+
+  @Test
+  public void serverGetFormattedElementOnTraceIdNotFound() {
+    Map<String, Object> data = new HashMap<>();
+    when(invocation.getContext(Const.TRACE_ID_NAME)).thenReturn("");
+    when(routingContext.data()).thenReturn(data);
+    data.put(RestConst.REST_INVOCATION_CONTEXT, invocation);
+
+    when(serverRequest.getHeader(Const.TRACE_ID_NAME)).thenReturn(null);
+    when(routingContext.request()).thenReturn(serverRequest);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), is("-"));
+
+    strBuilder = new StringBuilder();
+    when(invocation.getContext(Const.TRACE_ID_NAME)).thenReturn(null);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), is("-"));
+  }
+
+  @Test
+  public void clientGetFormattedElementOnTraceIdNotFound() {
+    clientContext.put(Const.TRACE_ID_NAME, null);
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getContext()).thenReturn(clientContext);
+
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), is("-"));
+  }
+
+  @Test
+  public void serverGetFormattedElementOnInvocationContextIsNull() {
+    when(routingContext.data()).thenReturn(null);
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.getHeader(Const.TRACE_ID_NAME)).thenReturn(null);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), is("-"));
+  }
+
+  @Test
+  public void clientGetFormattedElementOnInvocationContextIsNull() {
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getContext()).thenReturn(null);
+
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), is("-"));
+  }
+
+  @Test
+  public void serverGetFormattedElementOnDataIsNull() {
+    when(serverRequest.getHeader(Const.TRACE_ID_NAME)).thenReturn(null);
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(routingContext.data()).thenReturn(null);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertThat(strBuilder.toString(), is("-"));
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/TransportItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/TransportItemTest.java
new file mode 100644
index 0000000..e3da5f4
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/TransportItemTest.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.mockito.Mockito.when;
+
+import org.apache.servicecomb.core.Endpoint;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TransportItemTest {
+  private static final TransportItem ITEM = new TransportItem();
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private Invocation invocation;
+
+  private Endpoint endpoint;
+
+  @Before
+  public void initStrBuilder() {
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    invocation = Mockito.mock(Invocation.class);
+    endpoint = Mockito.mock(Endpoint.class);
+    accessLogEvent = new ServerAccessLogEvent();
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void clientFormattedElement() {
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getConfigTransportName()).thenReturn("rest");
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals("rest", strBuilder.toString());
+
+    strBuilder = new StringBuilder();
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getEndpoint()).thenReturn(endpoint);
+    when(endpoint.getEndpoint()).thenReturn("rest:xxx:30100");
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals("rest", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElement() {
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientConfigTransportNameIsNull() {
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getConfigTransportName()).thenReturn(null);
+
+    strBuilder = new StringBuilder();
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getEndpoint()).thenReturn(endpoint);
+    when(endpoint.getEndpoint()).thenReturn("rest:xxx:30100");
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals("rest", strBuilder.toString());
+  }
+
+  @Test
+  public void clientConfigTransportNameIsEmpty() {
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getConfigTransportName()).thenReturn("");
+    strBuilder = new StringBuilder();
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getEndpoint()).thenReturn(endpoint);
+    when(endpoint.getEndpoint()).thenReturn("rest:xxx:30100");
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals("rest", strBuilder.toString());
+  }
+
+  @Test
+  public void clientALLIsEmpty() {
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getConfigTransportName()).thenReturn(null);
+    strBuilder = new StringBuilder();
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getEndpoint()).thenReturn(null);
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/UrlPathItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/UrlPathItemTest.java
new file mode 100644
index 0000000..00183fe
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/UrlPathItemTest.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.servicecomb.common.rest.RestConst;
+import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.definition.OperationMeta;
+import org.apache.servicecomb.core.definition.SchemaMeta;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.swagger.models.Swagger;
+import io.vertx.core.http.HttpClientRequest;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.ext.web.RoutingContext;
+
+public class UrlPathItemTest {
+  private static final UrlPathItem ITEM = new UrlPathItem();
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext routingContext;
+
+  private Invocation invocation;
+
+  private HttpServerRequest serverRequest;
+
+  private OperationMeta operationMeta;
+
+  private SchemaMeta schemaMeta;
+
+  private Swagger swagger;
+
+  private RestClientRequestImpl restClientRequest;
+
+  private HttpClientRequest clientRequest;
+
+  @Before
+  public void initStrBuilder() {
+    accessLogEvent = new ServerAccessLogEvent();
+    routingContext = Mockito.mock(RoutingContext.class);
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    invocation = Mockito.mock(Invocation.class);
+    serverRequest = Mockito.mock(HttpServerRequest.class);
+    operationMeta = Mockito.mock(OperationMeta.class);
+    schemaMeta = Mockito.mock(SchemaMeta.class);
+    swagger = Mockito.mock(Swagger.class);
+    restClientRequest = Mockito.mock(RestClientRequestImpl.class);
+    clientRequest = Mockito.mock(HttpClientRequest.class);
+
+    accessLogEvent.setRoutingContext(routingContext);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void clientFormattedElement() {
+    String uri = "/base/test";
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getOperationMeta()).thenReturn(operationMeta);
+    when(invocation.getSchemaMeta()).thenReturn(schemaMeta);
+    when(schemaMeta.getSwagger()).thenReturn(swagger);
+    when(swagger.getBasePath()).thenReturn("/base");
+    when(operationMeta.getOperationPath()).thenReturn("/test");
+
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals(uri, strBuilder.toString());
+
+    strBuilder = new StringBuilder();
+    Map<String, Object> handlerContext = new HashMap<>();
+    handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest);
+    when(invocation.getOperationMeta()).thenReturn(null);
+    when(invocation.getSchemaMeta()).thenReturn(null);
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getHandlerContext()).thenReturn(handlerContext);
+    when(restClientRequest.getRequest()).thenReturn(clientRequest);
+    when(clientRequest.path()).thenReturn(uri);
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals(uri, strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElement() {
+    String uri = "/test";
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.path()).thenReturn(uri);
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertEquals(uri, strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnRequestIsNull() {
+    when(routingContext.request()).thenReturn(null);
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnRequestIsNull() {
+    Map<String, Object> handlerContext = new HashMap<>();
+    handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest);
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getHandlerContext()).thenReturn(handlerContext);
+    when(restClientRequest.getRequest()).thenReturn(null);
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnMethodIsNull() {
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.path()).thenReturn(null);
+    ITEM.appendFormattedItem(accessLogEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnMethodIsNull() {
+    Map<String, Object> handlerContext = new HashMap<>();
+    handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest);
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getHandlerContext()).thenReturn(handlerContext);
+    when(restClientRequest.getRequest()).thenReturn(clientRequest);
+    when(clientRequest.path()).thenReturn(null);
+    ITEM.appendFormattedItem(finishEvent, strBuilder);
+    Assert.assertEquals("-", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/UrlPathWithQueryItemTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/UrlPathWithQueryItemTest.java
new file mode 100644
index 0000000..b17fbb6
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/UrlPathWithQueryItemTest.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.element.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import org.apache.servicecomb.common.rest.RestConst;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.ext.web.RoutingContext;
+
+public class UrlPathWithQueryItemTest {
+
+  public static final UrlPathWithQueryItem ELEMENT = new UrlPathWithQueryItem();
+
+  private StringBuilder strBuilder;
+
+  private InvocationFinishEvent finishEvent;
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext routingContext;
+
+  private Invocation invocation;
+
+  private HttpServerRequest serverRequest;
+
+  @Before
+  public void initStrBuilder() {
+    accessLogEvent = new ServerAccessLogEvent();
+    routingContext = Mockito.mock(RoutingContext.class);
+    finishEvent = Mockito.mock(InvocationFinishEvent.class);
+    invocation = Mockito.mock(Invocation.class);
+    serverRequest = Mockito.mock(HttpServerRequest.class);
+
+    accessLogEvent.setRoutingContext(routingContext);
+    strBuilder = new StringBuilder();
+  }
+
+  @Test
+  public void serverFormattedElement() {
+    String uri = "uriTest";
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.uri()).thenReturn(uri);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals(uri, strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElement() {
+    String uri = "uriTest";
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getLocalContext(RestConst.REST_CLIENT_REQUEST_PATH)).thenReturn(uri);
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals(uri, strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnRequestIsNull() {
+    when(routingContext.request()).thenReturn(null);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnRequestIsNull() {
+    when(finishEvent.getInvocation()).thenReturn(null);
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnUriIsNull() {
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.uri()).thenReturn(null);
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnUriIsNull() {
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getLocalContext(RestConst.REST_CLIENT_REQUEST_PATH)).thenReturn(null);
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void serverFormattedElementOnUriIsEmpty() {
+    when(routingContext.request()).thenReturn(serverRequest);
+    when(serverRequest.uri()).thenReturn("");
+    ELEMENT.appendFormattedItem(accessLogEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+
+  @Test
+  public void clientFormattedElementOnUriIsEmpty() {
+    when(finishEvent.getInvocation()).thenReturn(invocation);
+    when(invocation.getLocalContext(RestConst.REST_CLIENT_REQUEST_PATH)).thenReturn("");
+    ELEMENT.appendFormattedItem(finishEvent, strBuilder);
+    assertEquals("-", strBuilder.toString());
+  }
+}
diff --git a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RemoteHostItem.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/UserDefinedAccessLogItem.java
similarity index 62%
copy from foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RemoteHostItem.java
copy to foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/UserDefinedAccessLogItem.java
index 0334009..da948c0 100644
--- a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RemoteHostItem.java
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/UserDefinedAccessLogItem.java
@@ -18,36 +18,31 @@
 package org.apache.servicecomb.foundation.log.core.element.impl;
 
 
-import org.apache.servicecomb.core.Endpoint;
 import org.apache.servicecomb.core.event.InvocationFinishEvent;
 import org.apache.servicecomb.core.event.ServerAccessLogEvent;
-import org.apache.servicecomb.foundation.common.net.URIEndpointObject;
 import org.apache.servicecomb.foundation.log.core.element.LogItem;
 
-import io.vertx.core.http.HttpServerRequest;
 import io.vertx.ext.web.RoutingContext;
 
-public class RemoteHostItem implements LogItem<RoutingContext> {
+/**
+ * For access log extension test
+ */
+public class UserDefinedAccessLogItem implements LogItem<RoutingContext> {
+  private String config;
 
-  public static final String EMPTY_RESULT = "-";
+  public UserDefinedAccessLogItem(String config) {
+    this.config = config;
+  }
 
   @Override
   public void appendFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) {
-    HttpServerRequest request = accessLogEvent.getRoutingContext().request();
-    if (null == request || null == request.remoteAddress()) {
-      builder.append(EMPTY_RESULT);
-      return;
-    }
-    builder.append(request.remoteAddress().host());
+    builder.append("user-defined-")
+        .append(config);
   }
 
   @Override
   public void appendFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) {
-    Endpoint endpoint = clientLogEvent.getInvocation().getEndpoint();
-    if (null == endpoint || null == endpoint.getAddress()) {
-      builder.append(EMPTY_RESULT);
-      return;
-    }
-    builder.append(((URIEndpointObject) endpoint.getAddress()).getHostOrIp());
+    builder.append("user-defined-")
+        .append(config);
   }
 }
diff --git a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RemoteHostItem.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/UserDefinedAccessLogItemLowPriority.java
similarity index 62%
copy from foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RemoteHostItem.java
copy to foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/UserDefinedAccessLogItemLowPriority.java
index 0334009..8104129 100644
--- a/foundations/foundation-log/src/main/java/org/apache/servicecomb/foundation/log/core/element/impl/RemoteHostItem.java
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/element/impl/UserDefinedAccessLogItemLowPriority.java
@@ -17,37 +17,25 @@
 
 package org.apache.servicecomb.foundation.log.core.element.impl;
 
-
-import org.apache.servicecomb.core.Endpoint;
 import org.apache.servicecomb.core.event.InvocationFinishEvent;
 import org.apache.servicecomb.core.event.ServerAccessLogEvent;
-import org.apache.servicecomb.foundation.common.net.URIEndpointObject;
 import org.apache.servicecomb.foundation.log.core.element.LogItem;
 
-import io.vertx.core.http.HttpServerRequest;
 import io.vertx.ext.web.RoutingContext;
 
-public class RemoteHostItem implements LogItem<RoutingContext> {
-
-  public static final String EMPTY_RESULT = "-";
+/**
+ * For access log extension test, will be overridden by {@link RemoteHostItem}("%h"),
+ * and takes no effect.
+ */
+public class UserDefinedAccessLogItemLowPriority implements LogItem<RoutingContext> {
 
   @Override
   public void appendFormattedItem(ServerAccessLogEvent accessLogEvent, StringBuilder builder) {
-    HttpServerRequest request = accessLogEvent.getRoutingContext().request();
-    if (null == request || null == request.remoteAddress()) {
-      builder.append(EMPTY_RESULT);
-      return;
-    }
-    builder.append(request.remoteAddress().host());
+    builder.append("OverriddenItem");
   }
 
   @Override
   public void appendFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) {
-    Endpoint endpoint = clientLogEvent.getInvocation().getEndpoint();
-    if (null == endpoint || null == endpoint.getAddress()) {
-      builder.append(EMPTY_RESULT);
-      return;
-    }
-    builder.append(((URIEndpointObject) endpoint.getAddress()).getHostOrIp());
+    builder.append("OverriddenItem");
   }
 }
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/parser/impl/TestCompositeExtendedAccessLogItemMeta.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/parser/impl/TestCompositeExtendedAccessLogItemMeta.java
new file mode 100644
index 0000000..a14913b
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/parser/impl/TestCompositeExtendedAccessLogItemMeta.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.parser.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.servicecomb.foundation.log.core.element.impl.UserDefinedAccessLogItem;
+import org.apache.servicecomb.foundation.log.core.parser.CompositeVertxRestLogItemMeta;
+import org.apache.servicecomb.foundation.log.core.parser.VertxRestLogItemMeta;
+
+
+public class TestCompositeExtendedAccessLogItemMeta extends CompositeVertxRestLogItemMeta {
+  private static final List<VertxRestLogItemMeta> META_LIST = new ArrayList<>();
+
+  static {
+    META_LIST.add(new VertxRestLogItemMeta("%{", "}user-defined", UserDefinedAccessLogItem::new));
+  }
+
+  @Override
+  public List<VertxRestLogItemMeta> getAccessLogItemMetas() {
+    return META_LIST;
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/parser/impl/TestSingleExtendedAccessLogItemMeta.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/parser/impl/TestSingleExtendedAccessLogItemMeta.java
new file mode 100644
index 0000000..46926a3
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/parser/impl/TestSingleExtendedAccessLogItemMeta.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.parser.impl;
+
+import org.apache.servicecomb.foundation.log.core.element.impl.UserDefinedAccessLogItemLowPriority;
+import org.apache.servicecomb.foundation.log.core.parser.VertxRestLogItemMeta;
+
+public class TestSingleExtendedAccessLogItemMeta extends VertxRestLogItemMeta {
+  public TestSingleExtendedAccessLogItemMeta() {
+    super("%h", null, config -> new UserDefinedAccessLogItemLowPriority(), 1);
+  }
+}
diff --git a/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/parser/impl/VertxRestAccessLogPatternParserTest.java b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/parser/impl/VertxRestAccessLogPatternParserTest.java
new file mode 100644
index 0000000..66dfeed
--- /dev/null
+++ b/foundations/foundation-log/src/test/java/org/apache/servicecomb/foundation/log/core/parser/impl/VertxRestAccessLogPatternParserTest.java
@@ -0,0 +1,362 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.foundation.log.core.parser.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.apache.servicecomb.core.event.ServerAccessLogEvent;
+import org.apache.servicecomb.foundation.log.core.element.LogItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.CookieItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.DatetimeConfigurableItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.DurationMillisecondItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.DurationSecondItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.FirstLineOfRequestItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.HttpMethodItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.HttpStatusItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.InvocationContextItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.LocalHostItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.LocalPortItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.QueryStringItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.RemoteHostItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.RequestHeaderItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.RequestProtocolItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.ResponseHeaderItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.ResponseSizeItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.TraceIdItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.TransportItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.UrlPathItem;
+import org.apache.servicecomb.foundation.log.core.element.impl.UrlPathWithQueryItem;
+import org.apache.servicecomb.foundation.log.core.parser.CompositeVertxRestLogItemMeta;
+import org.apache.servicecomb.foundation.log.core.parser.VertxRestLogItemMeta;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import io.vertx.ext.web.RoutingContext;
+import mockit.Deencapsulation;
+import mockit.Mock;
+import mockit.MockUp;
+
+public class VertxRestAccessLogPatternParserTest {
+  private static final String ROW_PATTERN = "[cs-method] %m %s%T%D%h%v%p%B%b%r%U%q"
+      + "cs-uri-stemcs-uri-querycs-uri%H%t%{yyyy MM dd HH:mm:ss zzz}t"
+      + "%{yyyy MM dd HH:mm:ss|GMT+0|en-US}t"
+      + "%{incoming-header}i"
+      + "%{outgoing-header}o"
+      + "%{cookie}C"
+      + "%SCB-traceId"
+      + "%{ctx}SCB-ctx"
+      + "%SCB-transport";
+
+  private static VertxRestLogPatternParser logPatternParser = new VertxRestLogPatternParser();
+
+  private ServerAccessLogEvent accessLogEvent;
+
+  private RoutingContext routingContext;
+
+
+  @Before
+  public void initStrBuilder() {
+    routingContext = Mockito.mock(RoutingContext.class);
+    accessLogEvent = new ServerAccessLogEvent();
+    accessLogEvent.setRoutingContext(routingContext);
+  }
+
+  @Test
+  public void testParsePatternFullTest() {
+    List<LogItem<RoutingContext>> result = logPatternParser.parsePattern(ROW_PATTERN);
+    assertEquals(29, result.size());
+    StringBuilder builder = new StringBuilder();
+    result.get(0).appendFormattedItem(accessLogEvent, builder);
+    assertEquals("[", builder.toString());
+    assertEquals(HttpMethodItem.class, result.get(1).getClass());
+    builder = new StringBuilder();
+    result.get(2).appendFormattedItem(accessLogEvent, builder);
+    assertEquals("] ", builder.toString());
+    assertEquals(HttpMethodItem.class, result.get(3).getClass());
+    builder = new StringBuilder();
+    result.get(4).appendFormattedItem(accessLogEvent, builder);
+    assertEquals(" ", builder.toString());
+    assertEquals(HttpStatusItem.class, result.get(5).getClass());
+    assertEquals(DurationSecondItem.class, result.get(6).getClass());
+    assertEquals(DurationMillisecondItem.class, result.get(7).getClass());
+    assertEquals(RemoteHostItem.class, result.get(8).getClass());
+    assertEquals(LocalHostItem.class, result.get(9).getClass());
+    assertEquals(LocalPortItem.class, result.get(10).getClass());
+    assertEquals(ResponseSizeItem.class, result.get(11).getClass());
+    assertEquals("0", ((ResponseSizeItem) result.get(11)).getZeroBytes());
+    assertEquals(ResponseSizeItem.class, result.get(12).getClass());
+    assertEquals("-", ((ResponseSizeItem) result.get(12)).getZeroBytes());
+    assertEquals(FirstLineOfRequestItem.class, result.get(13).getClass());
+    assertEquals(UrlPathItem.class, result.get(14).getClass());
+    assertEquals(QueryStringItem.class, result.get(15).getClass());
+    assertEquals(UrlPathItem.class, result.get(16).getClass());
+    assertEquals(QueryStringItem.class, result.get(17).getClass());
+    assertEquals(UrlPathWithQueryItem.class, result.get(18).getClass());
+    assertEquals(RequestProtocolItem.class, result.get(19).getClass());
+    assertEquals(DatetimeConfigurableItem.class, result.get(20).getClass());
+    assertEquals(DatetimeConfigurableItem.DEFAULT_DATETIME_PATTERN,
+        ((DatetimeConfigurableItem) result.get(20)).getPattern());
+    assertEquals(DatetimeConfigurableItem.DEFAULT_LOCALE, ((DatetimeConfigurableItem) result.get(20)).getLocale());
+    assertEquals(TimeZone.getDefault(), ((DatetimeConfigurableItem) result.get(20)).getTimezone());
+    assertEquals("yyyy MM dd HH:mm:ss zzz", ((DatetimeConfigurableItem) result.get(21)).getPattern());
+    assertEquals(DatetimeConfigurableItem.DEFAULT_LOCALE, ((DatetimeConfigurableItem) result.get(21)).getLocale());
+    assertEquals(TimeZone.getDefault(), ((DatetimeConfigurableItem) result.get(21)).getTimezone());
+    assertEquals("yyyy MM dd HH:mm:ss", ((DatetimeConfigurableItem) result.get(22)).getPattern());
+    assertEquals(Locale.forLanguageTag("en-US"), ((DatetimeConfigurableItem) result.get(22)).getLocale());
+    assertEquals(TimeZone.getTimeZone("GMT+0"), ((DatetimeConfigurableItem) result.get(22)).getTimezone());
+    assertEquals(RequestHeaderItem.class, result.get(23).getClass());
+    assertEquals("incoming-header", ((RequestHeaderItem) result.get(23)).getVarName());
+    assertEquals(ResponseHeaderItem.class, result.get(24).getClass());
+    assertEquals("outgoing-header", ((ResponseHeaderItem) result.get(24)).getVarName());
+    assertEquals(CookieItem.class, result.get(25).getClass());
+    assertEquals("cookie", ((CookieItem) result.get(25)).getVarName());
+    assertEquals(TraceIdItem.class, result.get(26).getClass());
+    assertEquals(InvocationContextItem.class, result.get(27).getClass());
+    assertEquals("ctx", ((InvocationContextItem) result.get(27)).getVarName());
+    assertEquals(TransportItem.class, result.get(28).getClass());
+  }
+
+  @Test
+  public void testParsePattern() {
+    String pattern = " %m  cs-uri-stem %{response-header}o ";
+    List<LogItem<RoutingContext>> result = logPatternParser.parsePattern(pattern);
+    assertEquals(7, result.size());
+    StringBuilder stringBuilder = new StringBuilder();
+    result.get(0).appendFormattedItem(accessLogEvent, stringBuilder);
+    assertEquals(" ", stringBuilder.toString());
+    assertEquals(HttpMethodItem.class, result.get(1).getClass());
+    stringBuilder = new StringBuilder();
+    result.get(2).appendFormattedItem(accessLogEvent, stringBuilder);
+    assertEquals("  ", stringBuilder.toString());
+    assertEquals(UrlPathItem.class, result.get(3).getClass());
+    stringBuilder = new StringBuilder();
+    result.get(4).appendFormattedItem(accessLogEvent, stringBuilder);
+    assertEquals(" ", stringBuilder.toString());
+    assertEquals(ResponseHeaderItem.class, result.get(5).getClass());
+    assertEquals("response-header", ((ResponseHeaderItem) result.get(5)).getVarName());
+    stringBuilder = new StringBuilder();
+    result.get(6).appendFormattedItem(accessLogEvent, stringBuilder);
+    assertEquals(" ", stringBuilder.toString());
+  }
+
+  @Test
+  public void testParsePatternWithNoBlank() {
+    String pattern = "%mcs-uri-stem%{response-header}o";
+    List<LogItem<RoutingContext>> result = logPatternParser.parsePattern(pattern);
+    assertEquals(3, result.size());
+
+    assertEquals(HttpMethodItem.class, result.get(0).getClass());
+    assertEquals(UrlPathItem.class, result.get(1).getClass());
+    assertEquals(ResponseHeaderItem.class, result.get(2).getClass());
+    assertEquals("response-header", ((ResponseHeaderItem) result.get(2)).getVarName());
+  }
+
+  @Test
+  public void testParsePatternComplex() {
+    String pattern = "%m  cs-uri-stem %{response-header}o abc cs-uri-query %s%{request} header}i plain cs-uri";
+    List<LogItem<RoutingContext>> result = logPatternParser.parsePattern(pattern);
+    assertEquals(12, result.size());
+
+    assertEquals(HttpMethodItem.class, result.get(0).getClass());
+    StringBuilder stringBuilder = new StringBuilder();
+    result.get(1).appendFormattedItem(accessLogEvent, stringBuilder);
+    assertEquals("  ", stringBuilder.toString());
+    assertEquals(UrlPathItem.class, result.get(2).getClass());
+    stringBuilder = new StringBuilder();
+    result.get(3).appendFormattedItem(accessLogEvent, stringBuilder);
+    assertEquals(" ", stringBuilder.toString());
+    assertEquals(ResponseHeaderItem.class, result.get(4).getClass());
+    assertEquals("response-header", ((ResponseHeaderItem) result.get(4)).getVarName());
+    stringBuilder = new StringBuilder();
+    result.get(5).appendFormattedItem(accessLogEvent, stringBuilder);
+    assertEquals(" abc ", stringBuilder.toString());
+    assertEquals(QueryStringItem.class, result.get(6).getClass());
+    stringBuilder = new StringBuilder();
+    result.get(7).appendFormattedItem(accessLogEvent, stringBuilder);
+    assertEquals(" ", stringBuilder.toString());
+    assertEquals(HttpStatusItem.class, result.get(8).getClass());
+    assertEquals(RequestHeaderItem.class, result.get(9).getClass());
+    assertEquals("request} header", ((RequestHeaderItem) result.get(9)).getVarName());
+    stringBuilder = new StringBuilder();
+    result.get(10).appendFormattedItem(accessLogEvent, stringBuilder);
+    assertEquals(" plain ", stringBuilder.toString());
+    assertEquals(UrlPathWithQueryItem.class, result.get(11).getClass());
+  }
+
+  Comparator<VertxRestLogItemMeta> comparator = VertxRestLogPatternParser.accessLogItemMetaComparator;
+
+  /**
+   * one factor test
+   */
+  @Test
+  public void testCompareMetaSimple() {
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestLogItemMeta(null, null, null, 0),
+            new VertxRestLogItemMeta(null, null, null, 1)
+        ) < 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestLogItemMeta(null, "}abc", null, 0),
+            new VertxRestLogItemMeta(null, null, null, 0)
+        ) < 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestLogItemMeta(null, "}abc", null, 0),
+            new VertxRestLogItemMeta(null, "}de", null, 0)
+        ) < 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestLogItemMeta(null, "}abc", null, 0),
+            new VertxRestLogItemMeta(null, "}ab", null, 0)
+        ) < 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestLogItemMeta("%abc", null, null, 0),
+            new VertxRestLogItemMeta("%de", null, null, 0)
+        ) < 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestLogItemMeta("%abc", null, null, 0),
+            new VertxRestLogItemMeta("%ab", null, null, 0)
+        ) < 0
+    );
+    Assert.assertEquals(0, comparator.compare(
+        new VertxRestLogItemMeta("%abc", null, null, 0),
+        new VertxRestLogItemMeta("%abc", null, null, 0)
+    ));
+  }
+
+  /**
+   * multiple factors test
+   */
+  @Test
+  public void testCompareMetaComplex() {
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestLogItemMeta("%bcd", "}ab", null, 0),
+            new VertxRestLogItemMeta("%abc", "}abc", null, 0)
+        ) > 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestLogItemMeta("%abc", null, null, 0),
+            new VertxRestLogItemMeta("%bcd", "}ab", null, 0)
+        ) > 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestLogItemMeta("%bcd", "}abc", null, 0),
+            new VertxRestLogItemMeta("%abc", "}abc", null, 0)
+        ) > 0
+    );
+    Assert.assertTrue(
+        comparator.compare(
+            new VertxRestLogItemMeta("%abc", "}abc", null, 1),
+            new VertxRestLogItemMeta("%ab", "}ab", null, 0)
+        ) > 0
+    );
+  }
+
+  @Test
+  public void testComparePlaceholderString() {
+    Assert.assertTrue(
+        VertxRestLogPatternParser.comparePlaceholderString("abc", "bbc") < 0
+    );
+    Assert.assertTrue(
+        VertxRestLogPatternParser.comparePlaceholderString("abc", "ab") < 0
+    );
+    Assert.assertEquals(0, VertxRestLogPatternParser.comparePlaceholderString("abc", "abc"));
+    Assert.assertTrue(
+        VertxRestLogPatternParser.comparePlaceholderString("bbc", "abc") > 0
+    );
+    Assert.assertTrue(
+        VertxRestLogPatternParser.comparePlaceholderString("ab", "abc") > 0
+    );
+  }
+
+  @Test
+  public void testExtendedVertxRestAccessLogItemCreator() {
+    final List<VertxRestLogItemMeta> metaList0 = new ArrayList<>();
+    metaList0.add(new VertxRestLogItemMeta("%{", "}abc", null));
+    metaList0.add(new VertxRestLogItemMeta("%{", "}a", null));
+    metaList0.add(new VertxRestLogItemMeta("%_", null, null, -1));
+
+    final List<VertxRestLogItemMeta> metaList1 = new ArrayList<>();
+    metaList0.add(new VertxRestLogItemMeta("%a", "}abc", null));
+    metaList0.add(new VertxRestLogItemMeta("%0", "}abc", null, 1));
+    metaList0.add(new VertxRestLogItemMeta("%m", null, null));
+
+    new MockUp<VertxRestLogPatternParser>() {
+      @Mock
+      List<VertxRestLogItemMeta> loadVertxRestLogItemMeta() {
+        List<VertxRestLogItemMeta> metaList = new ArrayList<>(1);
+        CompositeVertxRestLogItemMeta compositeMeta0 = new CompositeVertxRestLogItemMeta() {
+          @Override
+          public List<VertxRestLogItemMeta> getAccessLogItemMetas() {
+            return metaList0;
+          }
+        };
+        CompositeVertxRestLogItemMeta compositeMeta1 = new CompositeVertxRestLogItemMeta() {
+          @Override
+          public List<VertxRestLogItemMeta> getAccessLogItemMetas() {
+            return metaList1;
+          }
+        };
+        metaList.add(compositeMeta0);
+        metaList.add(compositeMeta1);
+        metaList.add(new VertxRestLogItemMeta("%{", null, null));
+        return metaList;
+      }
+    };
+
+    VertxRestLogPatternParser parser = new VertxRestLogPatternParser();
+
+    List<VertxRestLogItemMeta> accessLogItemMetaList =
+        Deencapsulation.getField(parser, "metaList");
+
+    assertEquals(7, accessLogItemMetaList.size());
+    assertEquals("%_", accessLogItemMetaList.get(0).getPrefix());
+    assertEquals("%a", accessLogItemMetaList.get(1).getPrefix());
+    assertEquals("}abc", accessLogItemMetaList.get(1).getSuffix());
+    assertEquals("%{", accessLogItemMetaList.get(2).getPrefix());
+    assertEquals("}abc", accessLogItemMetaList.get(2).getSuffix());
+    assertEquals("%{", accessLogItemMetaList.get(3).getPrefix());
+    assertEquals("}a", accessLogItemMetaList.get(3).getSuffix());
+    assertEquals("%m", accessLogItemMetaList.get(4).getPrefix());
+    assertNull(accessLogItemMetaList.get(4).getSuffix());
+    assertEquals("%{", accessLogItemMetaList.get(5).getPrefix());
+    assertNull(accessLogItemMetaList.get(5).getSuffix());
+    assertEquals("%0", accessLogItemMetaList.get(6).getPrefix());
+    assertEquals("}abc", accessLogItemMetaList.get(6).getSuffix());
+  }
+}
diff --git a/foundations/foundation-log/src/test/resources/META-INF/services/org.apache.servicecomb.foundation.log.core.parser.VertxRestLogItemMeta b/foundations/foundation-log/src/test/resources/META-INF/services/org.apache.servicecomb.foundation.log.core.parser.VertxRestLogItemMeta
new file mode 100644
index 0000000..1ef1185
--- /dev/null
+++ b/foundations/foundation-log/src/test/resources/META-INF/services/org.apache.servicecomb.foundation.log.core.parser.VertxRestLogItemMeta
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.apache.servicecomb.foundation.log.core.parser.impl.TestCompositeExtendedAccessLogItemMeta
+org.apache.servicecomb.foundation.log.core.parser.impl.TestSingleExtendedAccessLogItemMeta
\ No newline at end of file