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