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 2018/01/24 01:21:47 UTC

[incubator-servicecomb-java-chassis] 10/10: SCB-150 add Status Dimension output level support

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 bd87a7240eb1eb73d854d689b7776079fc4c33c5
Author: zhengyangyong <ya...@huawei.com>
AuthorDate: Wed Jan 17 15:36:37 2018 +0800

    SCB-150 add Status Dimension output level support
    
    Signed-off-by: zhengyangyong <ya...@huawei.com>
---
 .../common/rest/AbstractRestInvocation.java        |   2 +-
 .../org/apache/servicecomb/core/Invocation.java    |   4 +-
 .../core/metrics/InvocationFinishedEvent.java      |  11 +-
 .../core/provider/consumer/InvokerUtils.java       |   9 +-
 .../servicecomb/metrics/common/CallMetric.java     |   4 +-
 .../metrics/common/MetricsDimension.java           |  31 ++--
 .../servicecomb/metrics/core/MetricsConfig.java    |   2 +
 .../core/event/DefaultEventListenerManager.java    |  17 ++-
 .../event/InvocationFinishedEventListener.java     |  14 +-
 .../event/dimension/CodeGroupStatusConvertor.java  |  45 ++++++
 .../dimension/CodeStatusConvertor.java}            |   9 +-
 .../dimension/StatusConvertor.java}                |   6 +-
 .../event/dimension/StatusConvertorFactory.java    |  53 +++++++
 .../dimension/SuccessFailedStatusConvertor.java}   |  12 +-
 .../metrics/core/monitor/CallMonitor.java          |  90 ++++++------
 .../core/monitor/ConsumerInvocationMonitor.java    |   3 +-
 .../core/monitor/ProducerInvocationMonitor.java    |   3 +-
 .../metrics/core/TestEventAndRunner.java           |  72 ++++++----
 .../metrics/core/TestStatusDimension.java          | 158 +++++++++++++++++++++
 .../transport/highway/HighwayServerInvoke.java     |   2 +-
 20 files changed, 433 insertions(+), 114 deletions(-)

diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/AbstractRestInvocation.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/AbstractRestInvocation.java
index 0ee7999..36c1739 100644
--- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/AbstractRestInvocation.java
+++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/AbstractRestInvocation.java
@@ -189,7 +189,7 @@ public abstract class AbstractRestInvocation {
     invocation.next(resp -> {
       sendResponseQuietly(resp);
 
-      invocation.triggerFinishedEvent(resp.isSuccessed());
+      invocation.triggerFinishedEvent(resp.getStatusCode(), resp.isSuccessed());
       endMetrics();
     });
   }
diff --git a/core/src/main/java/org/apache/servicecomb/core/Invocation.java b/core/src/main/java/org/apache/servicecomb/core/Invocation.java
index 19241b8..81f25f8 100644
--- a/core/src/main/java/org/apache/servicecomb/core/Invocation.java
+++ b/core/src/main/java/org/apache/servicecomb/core/Invocation.java
@@ -202,12 +202,12 @@ public class Invocation extends SwaggerInvocation {
         operationMeta.getMicroserviceQualifiedName(), this.invocationType, startProcessingTime - startTime));
   }
 
-  public void triggerFinishedEvent(boolean success) {
+  public void triggerFinishedEvent(int statusCode, boolean success) {
     long finishedTime = System.nanoTime();
     EventUtils
         .triggerEvent(new InvocationFinishedEvent(operationMeta.getMicroserviceQualifiedName(),
             this.invocationType, finishedTime - startProcessingTime,
-            finishedTime - startTime, success));
+            finishedTime - startTime, statusCode, success));
   }
 
   public boolean isSync() {
diff --git a/core/src/main/java/org/apache/servicecomb/core/metrics/InvocationFinishedEvent.java b/core/src/main/java/org/apache/servicecomb/core/metrics/InvocationFinishedEvent.java
index ea54d67..4de6154 100644
--- a/core/src/main/java/org/apache/servicecomb/core/metrics/InvocationFinishedEvent.java
+++ b/core/src/main/java/org/apache/servicecomb/core/metrics/InvocationFinishedEvent.java
@@ -29,6 +29,8 @@ public class InvocationFinishedEvent implements Event {
 
   private final long totalElapsedNanoTime;
 
+  private final int statusCode;
+
   private final boolean success;
 
   public String getOperationName() {
@@ -47,18 +49,21 @@ public class InvocationFinishedEvent implements Event {
     return totalElapsedNanoTime;
   }
 
+  public int getStatusCode() {
+    return statusCode;
+  }
+
   public boolean isSuccess() {
     return success;
   }
 
   public InvocationFinishedEvent(String operationName, InvocationType invocationType,
-      long processElapsedNanoTime,
-      long totalElapsedNanoTime,
-      boolean success) {
+      long processElapsedNanoTime, long totalElapsedNanoTime, int statusCode, boolean success) {
     this.operationName = operationName;
     this.invocationType = invocationType;
     this.processElapsedNanoTime = processElapsedNanoTime;
     this.totalElapsedNanoTime = totalElapsedNanoTime;
+    this.statusCode = statusCode;
     this.success = success;
   }
 }
diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java
index e8b8247..2635ac3 100644
--- a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java
+++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java
@@ -63,6 +63,7 @@ public final class InvokerUtils {
 
   public static Response innerSyncInvoke(Invocation invocation) {
     boolean success = false;
+    int statusCode = 0;
     try {
       triggerStartedEvent(invocation);
       SyncResponseExecutor respExecutor = new SyncResponseExecutor();
@@ -72,6 +73,7 @@ public final class InvokerUtils {
 
       Response response = respExecutor.waitResponse();
       success = response.isSuccessed();
+      statusCode = response.getStatusCode();
       return response;
     } catch (Throwable e) {
       String msg =
@@ -79,7 +81,7 @@ public final class InvokerUtils {
       LOGGER.debug(msg, e);
       return Response.createConsumerFail(e);
     } finally {
-      invocation.triggerFinishedEvent(success);
+      invocation.triggerFinishedEvent(statusCode, success);
     }
   }
 
@@ -92,11 +94,12 @@ public final class InvokerUtils {
       invocation.setResponseExecutor(respExecutor);
 
       invocation.next(ar -> {
-        invocation.triggerFinishedEvent(ar.isSuccessed());
+        invocation.triggerFinishedEvent(ar.getStatusCode(), ar.isSuccessed());
         asyncResp.handle(ar);
       });
     } catch (Throwable e) {
-      invocation.triggerFinishedEvent(false);
+      //if throw exception,we can use 500 for status code ?
+      invocation.triggerFinishedEvent(500, false);
       LOGGER.error("invoke failed, {}", invocation.getOperationMeta().getMicroserviceQualifiedName());
       asyncResp.consumerFail(e);
     }
diff --git a/metrics/metrics-common/src/main/java/org/apache/servicecomb/metrics/common/CallMetric.java b/metrics/metrics-common/src/main/java/org/apache/servicecomb/metrics/common/CallMetric.java
index 56d0f99..4c5c862 100644
--- a/metrics/metrics-common/src/main/java/org/apache/servicecomb/metrics/common/CallMetric.java
+++ b/metrics/metrics-common/src/main/java/org/apache/servicecomb/metrics/common/CallMetric.java
@@ -45,7 +45,7 @@ public class CallMetric {
         return value;
       }
     }
-    return null;
+    return new LongMetricValue(dimensionValue, 0L, null);
   }
 
   public List<DoubleMetricValue> getTpsValues() {
@@ -58,7 +58,7 @@ public class CallMetric {
         return value;
       }
     }
-    return null;
+    return new DoubleMetricValue(dimensionValue, 0.0, null);
   }
 
   public CallMetric(String prefix) {
diff --git a/metrics/metrics-common/src/main/java/org/apache/servicecomb/metrics/common/MetricsDimension.java b/metrics/metrics-common/src/main/java/org/apache/servicecomb/metrics/common/MetricsDimension.java
index 23f1f40..904cc25 100644
--- a/metrics/metrics-common/src/main/java/org/apache/servicecomb/metrics/common/MetricsDimension.java
+++ b/metrics/metrics-common/src/main/java/org/apache/servicecomb/metrics/common/MetricsDimension.java
@@ -17,23 +17,30 @@
 
 package org.apache.servicecomb.metrics.common;
 
-import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException;
-
 public class MetricsDimension {
   public static final String DIMENSION_STATUS = "Status";
 
   public static final String DIMENSION_STATUS_ALL = "all";
 
-  public static final String DIMENSION_STATUS_SUCCESS = "success";
+  public static final String DIMENSION_STATUS_SUCCESS_FAILED_SUCCESS = "success";
+
+  public static final String DIMENSION_STATUS_SUCCESS_FAILED_FAILED = "failed";
+
+  public static final String DIMENSION_STATUS_CODE_GROUP_1XX = "1xx";
+
+  public static final String DIMENSION_STATUS_CODE_GROUP_2XX = "2xx";
+
+  public static final String DIMENSION_STATUS_CODE_GROUP_3XX = "3xx";
+
+  public static final String DIMENSION_STATUS_CODE_GROUP_4XX = "4xx";
+
+  public static final String DIMENSION_STATUS_CODE_GROUP_5XX = "5xx";
+
+  public static final String DIMENSION_STATUS_CODE_GROUP_OTHER = "xxx";
+
+  public static final String DIMENSION_STATUS_OUTPUT_LEVEL_SUCCESS_FAILED = "success_failed";
 
-  public static final String DIMENSION_STATUS_FAILED = "failed";
+  public static final String DIMENSION_STATUS_OUTPUT_LEVEL_CODE_GROUP = "code_group";
 
-  public static String[] getDimensionOptions(String dimension) {
-    if (DIMENSION_STATUS.equals(dimension)) {
-      return new String[] {MetricsDimension.DIMENSION_STATUS_ALL,
-          MetricsDimension.DIMENSION_STATUS_SUCCESS,
-          MetricsDimension.DIMENSION_STATUS_FAILED};
-    }
-    throw new ServiceCombException("illegal dimension key : " + dimension);
-  }
+  public static final String DIMENSION_STATUS_OUTPUT_LEVEL_CODE = "code";
 }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsConfig.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsConfig.java
index b903f15..cbf6455 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsConfig.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsConfig.java
@@ -19,4 +19,6 @@ package org.apache.servicecomb.metrics.core;
 
 public class MetricsConfig {
   public static final String METRICS_POLLING_TIME = "servicecomb.metrics.window_time";
+
+  public static final String METRICS_DIMENSION_STATUS_OUTPUT_LEVEL = "servicecomb.metrics.dimension.status.output_level";
 }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/DefaultEventListenerManager.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/DefaultEventListenerManager.java
index 42aa024..6dc6df4 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/DefaultEventListenerManager.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/DefaultEventListenerManager.java
@@ -19,18 +19,31 @@ package org.apache.servicecomb.metrics.core.event;
 
 import org.apache.servicecomb.foundation.common.event.EventListener;
 import org.apache.servicecomb.foundation.common.utils.EventUtils;
+import org.apache.servicecomb.metrics.common.MetricsDimension;
+import org.apache.servicecomb.metrics.core.MetricsConfig;
+import org.apache.servicecomb.metrics.core.event.dimension.StatusConvertorFactory;
 import org.apache.servicecomb.metrics.core.monitor.RegistryMonitor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import com.netflix.config.DynamicPropertyFactory;
+
 @Component
 public class DefaultEventListenerManager implements EventListenerManager {
 
   @Autowired
-  public DefaultEventListenerManager(RegistryMonitor registryMonitor) {
+  public DefaultEventListenerManager(RegistryMonitor registryMonitor, StatusConvertorFactory convertorFactory) {
+    this(registryMonitor, convertorFactory, DynamicPropertyFactory
+        .getInstance().getStringProperty(MetricsConfig.METRICS_DIMENSION_STATUS_OUTPUT_LEVEL,
+            MetricsDimension.DIMENSION_STATUS_OUTPUT_LEVEL_SUCCESS_FAILED).get());
+  }
+
+  public DefaultEventListenerManager(RegistryMonitor registryMonitor, StatusConvertorFactory convertorFactory,
+      String outputLevel) {
     this.registerEventListener(new InvocationStartedEventListener(registryMonitor));
     this.registerEventListener(new InvocationStartProcessingEventListener(registryMonitor));
-    this.registerEventListener(new InvocationFinishedEventListener(registryMonitor));
+    this.registerEventListener(
+        new InvocationFinishedEventListener(registryMonitor, convertorFactory.getConvertor(outputLevel)));
   }
 
   @Override
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/InvocationFinishedEventListener.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/InvocationFinishedEventListener.java
index 8d15eac..5b886b6 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/InvocationFinishedEventListener.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/InvocationFinishedEventListener.java
@@ -21,17 +21,20 @@ import org.apache.servicecomb.core.metrics.InvocationFinishedEvent;
 import org.apache.servicecomb.foundation.common.event.Event;
 import org.apache.servicecomb.foundation.common.event.EventListener;
 import org.apache.servicecomb.metrics.common.MetricsDimension;
+import org.apache.servicecomb.metrics.core.event.dimension.StatusConvertor;
 import org.apache.servicecomb.metrics.core.monitor.ConsumerInvocationMonitor;
 import org.apache.servicecomb.metrics.core.monitor.ProducerInvocationMonitor;
 import org.apache.servicecomb.metrics.core.monitor.RegistryMonitor;
 import org.apache.servicecomb.swagger.invocation.InvocationType;
 
 public class InvocationFinishedEventListener implements EventListener {
-
   private final RegistryMonitor registryMonitor;
 
-  public InvocationFinishedEventListener(RegistryMonitor registryMonitor) {
+  private final StatusConvertor convertor;
+
+  public InvocationFinishedEventListener(RegistryMonitor registryMonitor, StatusConvertor convertor) {
     this.registryMonitor = registryMonitor;
+    this.convertor = convertor;
   }
 
   @Override
@@ -42,17 +45,16 @@ public class InvocationFinishedEventListener implements EventListener {
   @Override
   public void process(Event data) {
     InvocationFinishedEvent event = (InvocationFinishedEvent) data;
+    String statusDimensionValue = convertor.convert(event.isSuccess(), event.getStatusCode());
     if (InvocationType.PRODUCER.equals(event.getInvocationType())) {
       ProducerInvocationMonitor monitor = registryMonitor.getProducerInvocationMonitor(event.getOperationName());
       monitor.getExecutionTime().update(event.getProcessElapsedNanoTime());
       monitor.getProducerLatency().update(event.getTotalElapsedNanoTime());
-      monitor.getProducerCall().increment(MetricsDimension.DIMENSION_STATUS,
-          event.isSuccess() ? MetricsDimension.DIMENSION_STATUS_SUCCESS : MetricsDimension.DIMENSION_STATUS_FAILED);
+      monitor.getProducerCall().increment(MetricsDimension.DIMENSION_STATUS, statusDimensionValue);
     } else {
       ConsumerInvocationMonitor monitor = registryMonitor.getConsumerInvocationMonitor(event.getOperationName());
       monitor.getConsumerLatency().update(event.getTotalElapsedNanoTime());
-      monitor.getConsumerCall().increment(MetricsDimension.DIMENSION_STATUS,
-          event.isSuccess() ? MetricsDimension.DIMENSION_STATUS_SUCCESS : MetricsDimension.DIMENSION_STATUS_FAILED);
+      monitor.getConsumerCall().increment(MetricsDimension.DIMENSION_STATUS, statusDimensionValue);
     }
   }
 }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/dimension/CodeGroupStatusConvertor.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/dimension/CodeGroupStatusConvertor.java
new file mode 100644
index 0000000..92b99d0
--- /dev/null
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/dimension/CodeGroupStatusConvertor.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.metrics.core.event.dimension;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.core.Response.Status.Family;
+
+import org.apache.servicecomb.metrics.common.MetricsDimension;
+
+public class CodeGroupStatusConvertor implements StatusConvertor {
+
+  private final Map<Family, String> families;
+
+  public CodeGroupStatusConvertor() {
+    this.families = new HashMap<>();
+    this.families.put(Family.INFORMATIONAL, MetricsDimension.DIMENSION_STATUS_CODE_GROUP_1XX);
+    this.families.put(Family.SUCCESSFUL, MetricsDimension.DIMENSION_STATUS_CODE_GROUP_2XX);
+    this.families.put(Family.REDIRECTION, MetricsDimension.DIMENSION_STATUS_CODE_GROUP_3XX);
+    this.families.put(Family.CLIENT_ERROR, MetricsDimension.DIMENSION_STATUS_CODE_GROUP_4XX);
+    this.families.put(Family.SERVER_ERROR, MetricsDimension.DIMENSION_STATUS_CODE_GROUP_5XX);
+    this.families.put(Family.OTHER, MetricsDimension.DIMENSION_STATUS_CODE_GROUP_OTHER);
+  }
+
+  @Override
+  public String convert(boolean success, int statusCode) {
+    return families.get(Family.familyOf(statusCode));
+  }
+}
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsConfig.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/dimension/CodeStatusConvertor.java
similarity index 77%
copy from metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsConfig.java
copy to metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/dimension/CodeStatusConvertor.java
index b903f15..b01f5da 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsConfig.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/dimension/CodeStatusConvertor.java
@@ -15,8 +15,11 @@
  * limitations under the License.
  */
 
-package org.apache.servicecomb.metrics.core;
+package org.apache.servicecomb.metrics.core.event.dimension;
 
-public class MetricsConfig {
-  public static final String METRICS_POLLING_TIME = "servicecomb.metrics.window_time";
+public class CodeStatusConvertor implements StatusConvertor {
+  @Override
+  public String convert(boolean success, int statusCode) {
+    return String.valueOf(statusCode);
+  }
 }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsConfig.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/dimension/StatusConvertor.java
similarity index 83%
copy from metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsConfig.java
copy to metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/dimension/StatusConvertor.java
index b903f15..17023b6 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsConfig.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/dimension/StatusConvertor.java
@@ -15,8 +15,8 @@
  * limitations under the License.
  */
 
-package org.apache.servicecomb.metrics.core;
+package org.apache.servicecomb.metrics.core.event.dimension;
 
-public class MetricsConfig {
-  public static final String METRICS_POLLING_TIME = "servicecomb.metrics.window_time";
+public interface StatusConvertor {
+  String convert(boolean success, int statusCode);
 }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/dimension/StatusConvertorFactory.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/dimension/StatusConvertorFactory.java
new file mode 100644
index 0000000..369cda4
--- /dev/null
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/dimension/StatusConvertorFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.metrics.core.event.dimension;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.servicecomb.metrics.common.MetricsDimension;
+import org.apache.servicecomb.metrics.core.MetricsConfig;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class StatusConvertorFactory {
+
+  private final Map<String, Supplier<StatusConvertor>> suppliers;
+
+  public StatusConvertorFactory() {
+    this.suppliers = new HashMap<>();
+    this.suppliers.put(MetricsDimension.DIMENSION_STATUS_OUTPUT_LEVEL_CODE, CodeStatusConvertor::new);
+    this.suppliers.put(MetricsDimension.DIMENSION_STATUS_OUTPUT_LEVEL_CODE_GROUP, CodeGroupStatusConvertor::new);
+    this.suppliers
+        .put(MetricsDimension.DIMENSION_STATUS_OUTPUT_LEVEL_SUCCESS_FAILED, SuccessFailedStatusConvertor::new);
+  }
+
+  public StatusConvertor getConvertor(String outputLevel) {
+    if (suppliers.containsKey(outputLevel)) {
+      return suppliers.get(outputLevel).get();
+    }
+    LoggerFactory.getLogger(StatusConvertorFactory.class).error("unknown config value of " +
+        MetricsConfig.METRICS_DIMENSION_STATUS_OUTPUT_LEVEL + " : " + outputLevel
+        + ", use default level : " +
+        MetricsDimension.DIMENSION_STATUS_OUTPUT_LEVEL_SUCCESS_FAILED + " replace it");
+    //return default
+    return suppliers.get(MetricsDimension.DIMENSION_STATUS_OUTPUT_LEVEL_SUCCESS_FAILED).get();
+  }
+}
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsConfig.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/dimension/SuccessFailedStatusConvertor.java
similarity index 65%
copy from metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsConfig.java
copy to metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/dimension/SuccessFailedStatusConvertor.java
index b903f15..c2cf998 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsConfig.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/event/dimension/SuccessFailedStatusConvertor.java
@@ -15,8 +15,14 @@
  * limitations under the License.
  */
 
-package org.apache.servicecomb.metrics.core;
+package org.apache.servicecomb.metrics.core.event.dimension;
 
-public class MetricsConfig {
-  public static final String METRICS_POLLING_TIME = "servicecomb.metrics.window_time";
+import org.apache.servicecomb.metrics.common.MetricsDimension;
+
+public class SuccessFailedStatusConvertor implements StatusConvertor {
+  @Override
+  public String convert(boolean success, int statusCode) {
+    return success ? MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_SUCCESS
+        : MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_FAILED;
+  }
 }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/monitor/CallMonitor.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/monitor/CallMonitor.java
index 068776e..2e61440 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/monitor/CallMonitor.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/monitor/CallMonitor.java
@@ -19,7 +19,9 @@ package org.apache.servicecomb.metrics.core.monitor;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
+import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
 import org.apache.servicecomb.metrics.common.CallMetric;
 import org.apache.servicecomb.metrics.common.DoubleMetricValue;
 import org.apache.servicecomb.metrics.common.LongMetricValue;
@@ -33,61 +35,61 @@ import com.netflix.servo.monitor.StepCounter;
 public class CallMonitor {
   private final String prefix;
 
-  private final List<BasicCounter> totalCounters;
+  private final Map<String, Map<String, DimensionCounter>> dimensionCounters;
 
-  private final List<StepCounter> tpsCounters;
-
-  public CallMonitor(String prefix, String... dimensionKeys) {
+  public CallMonitor(String prefix) {
     this.prefix = prefix;
-    this.totalCounters = new ArrayList<>();
-    this.tpsCounters = new ArrayList<>();
-    if (dimensionKeys.length == 0) {
-      this.totalCounters.add(new BasicCounter(MonitorConfig.builder(prefix + ".total").build()));
-      this.tpsCounters.add(new StepCounter(MonitorConfig.builder(prefix + ".tps").build()));
-    } else {
-      for (String dimensionKey : dimensionKeys) {
-        for (String option : MetricsDimension.getDimensionOptions(dimensionKey)) {
-          this.totalCounters
-              .add(new BasicCounter(MonitorConfig.builder(prefix + ".total").withTag(dimensionKey, option).build()));
-          this.tpsCounters
-              .add(new StepCounter(MonitorConfig.builder(prefix + ".tps").withTag(dimensionKey, option).build()));
-        }
-      }
-    }
+    this.dimensionCounters = new ConcurrentHashMapEx<>();
+    this.dimensionCounters.put(MetricsDimension.DIMENSION_STATUS, new ConcurrentHashMapEx<>());
   }
 
-  public void increment() {
-    for (int i = 0; i < totalCounters.size(); i++) {
-      totalCounters.get(i).increment();
-      tpsCounters.get(i).increment();
-    }
-  }
-
-  public void increment(String dimensionKey, String dimensionValue) {
-    for (int i = 0; i < totalCounters.size(); i++) {
-      BasicCounter totalCounter = totalCounters.get(i);
-      if (MonitorUtils.containsTagValue(totalCounter, dimensionKey, dimensionValue)) {
-        totalCounter.increment();
-      }
-      StepCounter tpsCounter = tpsCounters.get(i);
-      if (MonitorUtils.containsTagValue(tpsCounter, dimensionKey, dimensionValue)) {
-        tpsCounter.increment();
-      }
+  public void increment(String dimensionKey, String... dimensionValues) {
+    for (String dimensionValue : dimensionValues) {
+      DimensionCounter counter = dimensionCounters.get(dimensionKey)
+          .computeIfAbsent(dimensionValue, d -> new DimensionCounter(
+              new BasicCounter(MonitorConfig.builder(prefix + ".total").withTag(dimensionKey, dimensionValue).build()),
+              new StepCounter(MonitorConfig.builder(prefix + ".tps").withTag(dimensionKey, dimensionValue).build())));
+      counter.increment();
     }
   }
 
   public CallMetric toMetric(int windowTimeIndex) {
     List<LongMetricValue> totalValues = new ArrayList<>();
     List<DoubleMetricValue> tpsValues = new ArrayList<>();
-    for (int i = 0; i < totalCounters.size(); i++) {
-      BasicCounter totalCounter = totalCounters.get(i);
-      totalValues.add(new LongMetricValue(totalCounter.getValue(windowTimeIndex).longValue(),
-          MonitorUtils.convertTags(totalCounter)));
-      StepCounter tpsCounter = tpsCounters.get(i);
-      tpsValues.add(
-          new DoubleMetricValue(MonitorUtils.adjustValue(tpsCounter.getValue(windowTimeIndex).doubleValue()),
-              MonitorUtils.convertTags(tpsCounter)));
+    for (Map<String, DimensionCounter> dimensionCounter : dimensionCounters.values()) {
+      for (DimensionCounter counter : dimensionCounter.values()) {
+        totalValues.add(new LongMetricValue(counter.getTotal().getValue(windowTimeIndex).longValue(),
+            MonitorUtils.convertTags(counter.getTotal())));
+        tpsValues.add(
+            new DoubleMetricValue(MonitorUtils.adjustValue(counter.getTps().getValue(windowTimeIndex).doubleValue()),
+                MonitorUtils.convertTags(counter.getTps())));
+      }
     }
+
     return new CallMetric(this.prefix, totalValues, tpsValues);
   }
+
+  class DimensionCounter {
+    private final BasicCounter total;
+
+    private final StepCounter tps;
+
+    public BasicCounter getTotal() {
+      return total;
+    }
+
+    public StepCounter getTps() {
+      return tps;
+    }
+
+    public DimensionCounter(BasicCounter total, StepCounter tps) {
+      this.total = total;
+      this.tps = tps;
+    }
+
+    public void increment() {
+      total.increment();
+      tps.increment();
+    }
+  }
 }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/monitor/ConsumerInvocationMonitor.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/monitor/ConsumerInvocationMonitor.java
index 9a09551..1f2f247 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/monitor/ConsumerInvocationMonitor.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/monitor/ConsumerInvocationMonitor.java
@@ -20,7 +20,6 @@ package org.apache.servicecomb.metrics.core.monitor;
 
 import org.apache.servicecomb.metrics.common.ConsumerInvocationMetric;
 import org.apache.servicecomb.metrics.common.MetricsConst;
-import org.apache.servicecomb.metrics.common.MetricsDimension;
 
 public class ConsumerInvocationMonitor extends InvocationMonitor {
   private final TimerMonitor consumerLatency;
@@ -38,7 +37,7 @@ public class ConsumerInvocationMonitor extends InvocationMonitor {
   public ConsumerInvocationMonitor(String operationName) {
     super(operationName, String.format(MetricsConst.CONSUMER_PREFIX_TEMPLATE, operationName));
     this.consumerLatency = new TimerMonitor(this.getPrefix() + ".consumerLatency");
-    this.consumerCall = new CallMonitor(this.getPrefix() + ".consumerCall", MetricsDimension.DIMENSION_STATUS);
+    this.consumerCall = new CallMonitor(this.getPrefix() + ".consumerCall");
   }
 
   public ConsumerInvocationMetric toMetric(int windowTimeIndex) {
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/monitor/ProducerInvocationMonitor.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/monitor/ProducerInvocationMonitor.java
index 50a508c..9c77ec8 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/monitor/ProducerInvocationMonitor.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/monitor/ProducerInvocationMonitor.java
@@ -18,7 +18,6 @@
 package org.apache.servicecomb.metrics.core.monitor;
 
 import org.apache.servicecomb.metrics.common.MetricsConst;
-import org.apache.servicecomb.metrics.common.MetricsDimension;
 import org.apache.servicecomb.metrics.common.ProducerInvocationMetric;
 
 import com.netflix.servo.monitor.BasicCounter;
@@ -61,7 +60,7 @@ public class ProducerInvocationMonitor extends InvocationMonitor {
     this.lifeTimeInQueue = new TimerMonitor(this.getPrefix() + ".lifeTimeInQueue");
     this.executionTime = new TimerMonitor(this.getPrefix() + ".executionTime");
     this.producerLatency = new TimerMonitor(this.getPrefix() + ".producerLatency");
-    this.producerCall = new CallMonitor(this.getPrefix() + ".producerCall", MetricsDimension.DIMENSION_STATUS);
+    this.producerCall = new CallMonitor(this.getPrefix() + ".producerCall");
   }
 
   public ProducerInvocationMetric toMetric(int windowTimeIndex) {
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestEventAndRunner.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestEventAndRunner.java
index 8e87439..85cf463 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestEventAndRunner.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestEventAndRunner.java
@@ -36,6 +36,7 @@ import org.apache.servicecomb.foundation.common.utils.EventUtils;
 import org.apache.servicecomb.metrics.common.MetricsDimension;
 import org.apache.servicecomb.metrics.common.RegistryMetric;
 import org.apache.servicecomb.metrics.core.event.DefaultEventListenerManager;
+import org.apache.servicecomb.metrics.core.event.dimension.StatusConvertorFactory;
 import org.apache.servicecomb.metrics.core.monitor.DefaultSystemMonitor;
 import org.apache.servicecomb.metrics.core.monitor.RegistryMonitor;
 import org.apache.servicecomb.metrics.core.publish.DefaultDataSource;
@@ -74,7 +75,8 @@ public class TestEventAndRunner {
     Assert.assertEquals(intervals.size(), 3);
     Assert.assertThat(intervals, containsInAnyOrder(Arrays.asList(1000L, 2000L, 3000L).toArray()));
 
-    new DefaultEventListenerManager(monitor);
+    new DefaultEventListenerManager(monitor, new StatusConvertorFactory(),
+        MetricsDimension.DIMENSION_STATUS_OUTPUT_LEVEL_SUCCESS_FAILED);
 
     //fun1 is a PRODUCER invocation call 2 time and all is completed
     //two time success
@@ -84,7 +86,7 @@ public class TestEventAndRunner {
             TimeUnit.MILLISECONDS.toNanos(100)));
     EventUtils
         .triggerEvent(new InvocationFinishedEvent("fun1", InvocationType.PRODUCER,
-            TimeUnit.MILLISECONDS.toNanos(200), TimeUnit.MILLISECONDS.toNanos(300), true));
+            TimeUnit.MILLISECONDS.toNanos(200), TimeUnit.MILLISECONDS.toNanos(300), 200, true));
 
     EventUtils.triggerEvent(new InvocationStartedEvent("fun1", InvocationType.PRODUCER, System.nanoTime()));
     EventUtils.triggerEvent(
@@ -92,7 +94,7 @@ public class TestEventAndRunner {
             TimeUnit.MILLISECONDS.toNanos(300)));
     EventUtils
         .triggerEvent(new InvocationFinishedEvent("fun1", InvocationType.PRODUCER,
-            TimeUnit.MILLISECONDS.toNanos(400), TimeUnit.MILLISECONDS.toNanos(700), false));
+            TimeUnit.MILLISECONDS.toNanos(400), TimeUnit.MILLISECONDS.toNanos(700), 500, false));
 
     //==========================================================================
 
@@ -116,7 +118,7 @@ public class TestEventAndRunner {
             TimeUnit.MILLISECONDS.toNanos(100)));
     EventUtils
         .triggerEvent(new InvocationFinishedEvent("fun2", InvocationType.CONSUMER,
-            TimeUnit.MILLISECONDS.toNanos(200), TimeUnit.MILLISECONDS.toNanos(300), true));
+            TimeUnit.MILLISECONDS.toNanos(200), TimeUnit.MILLISECONDS.toNanos(300), 200, true));
 
     //==========================================================================
 
@@ -159,16 +161,20 @@ public class TestEventAndRunner {
     Assert.assertEquals(4, model.getInstanceMetric().getProducerMetric().getProducerCall()
         .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_ALL).getValue(), 0);
     Assert.assertEquals(1, model.getInstanceMetric().getProducerMetric().getProducerCall()
-        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS).getValue(), 0);
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_SUCCESS)
+        .getValue(), 0);
     Assert.assertEquals(1, model.getInstanceMetric().getProducerMetric().getProducerCall()
-        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_FAILED).getValue(), 0);
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_FAILED)
+        .getValue(), 0);
 
     Assert.assertEquals(4, model.getInstanceMetric().getProducerMetric().getProducerCall()
         .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_ALL).getValue(), 0);
     Assert.assertEquals(1, model.getInstanceMetric().getProducerMetric().getProducerCall()
-        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS).getValue(), 0);
+        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_SUCCESS)
+        .getValue(), 0);
     Assert.assertEquals(1, model.getInstanceMetric().getProducerMetric().getProducerCall()
-        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_FAILED).getValue(), 0);
+        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_FAILED)
+        .getValue(), 0);
 
     Assert.assertEquals(1, model.getInstanceMetric().getConsumerMetric().getConsumerLatency().getCount());
     Assert.assertEquals(300, model.getInstanceMetric().getConsumerMetric().getConsumerLatency().getTotal(),
@@ -183,16 +189,20 @@ public class TestEventAndRunner {
     Assert.assertEquals(1, model.getInstanceMetric().getConsumerMetric().getConsumerCall()
         .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_ALL).getValue(), 0);
     Assert.assertEquals(1, model.getInstanceMetric().getConsumerMetric().getConsumerCall()
-        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS).getValue(), 0);
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_SUCCESS)
+        .getValue(), 0);
     Assert.assertEquals(0, model.getInstanceMetric().getConsumerMetric().getConsumerCall()
-        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_FAILED).getValue(), 0);
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_FAILED)
+        .getValue(), 0);
 
     Assert.assertEquals(1, model.getInstanceMetric().getConsumerMetric().getConsumerCall()
         .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_ALL).getValue(), 0);
     Assert.assertEquals(1, model.getInstanceMetric().getConsumerMetric().getConsumerCall()
-        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS).getValue(), 0);
+        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_SUCCESS)
+        .getValue(), 0);
     Assert.assertEquals(0, model.getInstanceMetric().getConsumerMetric().getConsumerCall()
-        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_FAILED).getValue(), 0);
+        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_FAILED)
+        .getValue(), 0);
 
     //check ProducerMetrics
     Assert.assertEquals(0, model.getProducerMetrics().get("fun1").getWaitInQueue());
@@ -217,16 +227,20 @@ public class TestEventAndRunner {
     Assert.assertEquals(2, model.getProducerMetrics().get("fun1").getProducerCall()
         .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_ALL).getValue(), 0);
     Assert.assertEquals(1, model.getProducerMetrics().get("fun1").getProducerCall()
-        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS).getValue(), 0);
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_SUCCESS)
+        .getValue(), 0);
     Assert.assertEquals(1, model.getProducerMetrics().get("fun1").getProducerCall()
-        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_FAILED).getValue(), 0);
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_FAILED)
+        .getValue(), 0);
 
     Assert.assertEquals(2, model.getProducerMetrics().get("fun1").getProducerCall()
         .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_ALL).getValue(), 0);
     Assert.assertEquals(1, model.getProducerMetrics().get("fun1").getProducerCall()
-        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS).getValue(), 0);
+        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_SUCCESS)
+        .getValue(), 0);
     Assert.assertEquals(1, model.getProducerMetrics().get("fun1").getProducerCall()
-        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_FAILED).getValue(), 0);
+        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_FAILED)
+        .getValue(), 0);
 
     //fun3
     Assert.assertEquals(0, model.getProducerMetrics().get("fun3").getWaitInQueue());
@@ -251,16 +265,20 @@ public class TestEventAndRunner {
     Assert.assertEquals(1, model.getProducerMetrics().get("fun3").getProducerCall()
         .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_ALL).getValue(), 0);
     Assert.assertEquals(0, model.getProducerMetrics().get("fun3").getProducerCall()
-        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS).getValue(), 0);
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_SUCCESS)
+        .getValue(), 0);
     Assert.assertEquals(0, model.getProducerMetrics().get("fun3").getProducerCall()
-        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_FAILED).getValue(), 0);
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_FAILED)
+        .getValue(), 0);
 
     Assert.assertEquals(1, model.getProducerMetrics().get("fun3").getProducerCall()
         .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_ALL).getValue(), 0);
     Assert.assertEquals(0, model.getProducerMetrics().get("fun3").getProducerCall()
-        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS).getValue(), 0);
+        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_SUCCESS)
+        .getValue(), 0);
     Assert.assertEquals(0, model.getProducerMetrics().get("fun3").getProducerCall()
-        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_FAILED).getValue(), 0);
+        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_FAILED)
+        .getValue(), 0);
 
     //check ConsumerMetrics
     //no need
@@ -273,19 +291,23 @@ public class TestEventAndRunner {
     Assert.assertEquals(1, model.getConsumerMetrics().get("fun2").getConsumerCall()
         .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_ALL).getValue(), 0);
     Assert.assertEquals(1, model.getConsumerMetrics().get("fun2").getConsumerCall()
-        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS).getValue(), 0);
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_SUCCESS)
+        .getValue(), 0);
     Assert.assertEquals(0, model.getConsumerMetrics().get("fun2").getConsumerCall()
-        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_FAILED).getValue(), 0);
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_FAILED)
+        .getValue(), 0);
 
     Assert.assertEquals(1, model.getConsumerMetrics().get("fun2").getConsumerCall()
         .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_ALL).getValue(), 0);
     Assert.assertEquals(1, model.getConsumerMetrics().get("fun2").getConsumerCall()
-        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS).getValue(), 0);
+        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_SUCCESS)
+        .getValue(), 0);
     Assert.assertEquals(0, model.getConsumerMetrics().get("fun2").getConsumerCall()
-        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_FAILED).getValue(), 0);
+        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_SUCCESS_FAILED_FAILED)
+        .getValue(), 0);
 
     Map<String, Number> metrics = model.toMap();
-    Assert.assertEquals(120, metrics.size());
+    Assert.assertEquals(108, metrics.size());
 
     Assert.assertEquals(1.0, model.getInstanceMetric().getSystemMetric().getCpuLoad(), 0);
     Assert.assertEquals(2, model.getInstanceMetric().getSystemMetric().getCpuRunningThreads(), 0);
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestStatusDimension.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestStatusDimension.java
new file mode 100644
index 0000000..0a113fb
--- /dev/null
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestStatusDimension.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.metrics.core;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.servicecomb.core.metrics.InvocationFinishedEvent;
+import org.apache.servicecomb.core.metrics.InvocationStartProcessingEvent;
+import org.apache.servicecomb.core.metrics.InvocationStartedEvent;
+import org.apache.servicecomb.foundation.common.utils.EventUtils;
+import org.apache.servicecomb.metrics.common.MetricsDimension;
+import org.apache.servicecomb.metrics.common.RegistryMetric;
+import org.apache.servicecomb.metrics.core.event.DefaultEventListenerManager;
+import org.apache.servicecomb.metrics.core.event.dimension.StatusConvertorFactory;
+import org.apache.servicecomb.metrics.core.monitor.DefaultSystemMonitor;
+import org.apache.servicecomb.metrics.core.monitor.RegistryMonitor;
+import org.apache.servicecomb.metrics.core.publish.DefaultDataSource;
+import org.apache.servicecomb.swagger.invocation.InvocationType;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestStatusDimension {
+
+  @Test
+  public void testCodeGroupDimension() throws InterruptedException {
+    RegistryMetric model = prepare(MetricsDimension.DIMENSION_STATUS_OUTPUT_LEVEL_CODE_GROUP);
+
+    Assert.assertEquals(5, model.getInstanceMetric().getProducerMetric().getProducerCall()
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_ALL).getValue(), 0);
+    Assert.assertEquals(1, model.getInstanceMetric().getProducerMetric().getProducerCall()
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_CODE_GROUP_2XX)
+        .getValue(), 0);
+    Assert.assertEquals(1, model.getInstanceMetric().getProducerMetric().getProducerCall()
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_CODE_GROUP_3XX)
+        .getValue(), 0);
+    Assert.assertEquals(1, model.getInstanceMetric().getProducerMetric().getProducerCall()
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_CODE_GROUP_4XX)
+        .getValue(), 0);
+    Assert.assertEquals(1, model.getInstanceMetric().getProducerMetric().getProducerCall()
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_CODE_GROUP_5XX)
+        .getValue(), 0);
+    Assert.assertEquals(1, model.getInstanceMetric().getProducerMetric().getProducerCall()
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_CODE_GROUP_OTHER)
+        .getValue(), 0);
+
+    Assert.assertEquals(1, model.getInstanceMetric().getConsumerMetric().getConsumerCall()
+        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_ALL).getValue(), 0);
+    Assert.assertEquals(1, model.getInstanceMetric().getConsumerMetric().getConsumerCall()
+        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_CODE_GROUP_2XX)
+        .getValue(), 0);
+  }
+
+  @Test
+  public void testCodeDimension() throws InterruptedException {
+    RegistryMetric model = prepare(MetricsDimension.DIMENSION_STATUS_OUTPUT_LEVEL_CODE);
+
+    Assert.assertEquals(5, model.getInstanceMetric().getProducerMetric().getProducerCall()
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_ALL).getValue(), 0);
+    Assert.assertEquals(1, model.getInstanceMetric().getProducerMetric().getProducerCall()
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, "222")
+        .getValue(), 0);
+    Assert.assertEquals(1, model.getInstanceMetric().getProducerMetric().getProducerCall()
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, "333")
+        .getValue(), 0);
+    Assert.assertEquals(1, model.getInstanceMetric().getProducerMetric().getProducerCall()
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, "444")
+        .getValue(), 0);
+    Assert.assertEquals(1, model.getInstanceMetric().getProducerMetric().getProducerCall()
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, "555")
+        .getValue(), 0);
+    Assert.assertEquals(1, model.getInstanceMetric().getProducerMetric().getProducerCall()
+        .getTpsValue(MetricsDimension.DIMENSION_STATUS, "666")
+        .getValue(), 0);
+
+    Assert.assertEquals(1, model.getInstanceMetric().getConsumerMetric().getConsumerCall()
+        .getTotalValue(MetricsDimension.DIMENSION_STATUS, MetricsDimension.DIMENSION_STATUS_ALL).getValue(), 0);
+    Assert.assertEquals(1, model.getInstanceMetric().getConsumerMetric().getConsumerCall()
+        .getTotalValue(MetricsDimension.DIMENSION_STATUS, "200")
+        .getValue(), 0);
+  }
+
+  private RegistryMetric prepare(String outputLevel) throws InterruptedException {
+    DefaultSystemMonitor systemMonitor = new DefaultSystemMonitor();
+    RegistryMonitor monitor = new RegistryMonitor(systemMonitor);
+    DefaultDataSource dataSource = new DefaultDataSource(monitor, "1000,2000,3000");
+
+    new DefaultEventListenerManager(monitor, new StatusConvertorFactory(), outputLevel);
+
+    EventUtils.triggerEvent(new InvocationStartedEvent("fun1", InvocationType.PRODUCER, System.nanoTime()));
+    EventUtils.triggerEvent(
+        new InvocationStartProcessingEvent("fun1", InvocationType.PRODUCER,
+            TimeUnit.MILLISECONDS.toNanos(100)));
+    EventUtils
+        .triggerEvent(new InvocationFinishedEvent("fun1", InvocationType.PRODUCER,
+            TimeUnit.MILLISECONDS.toNanos(200), TimeUnit.MILLISECONDS.toNanos(300), 222, true));
+
+    EventUtils.triggerEvent(new InvocationStartedEvent("fun1", InvocationType.PRODUCER, System.nanoTime()));
+    EventUtils.triggerEvent(
+        new InvocationStartProcessingEvent("fun1", InvocationType.PRODUCER,
+            TimeUnit.MILLISECONDS.toNanos(100)));
+    EventUtils
+        .triggerEvent(new InvocationFinishedEvent("fun1", InvocationType.PRODUCER,
+            TimeUnit.MILLISECONDS.toNanos(200), TimeUnit.MILLISECONDS.toNanos(300), 333, false));
+
+    EventUtils.triggerEvent(new InvocationStartedEvent("fun1", InvocationType.PRODUCER, System.nanoTime()));
+    EventUtils.triggerEvent(
+        new InvocationStartProcessingEvent("fun1", InvocationType.PRODUCER,
+            TimeUnit.MILLISECONDS.toNanos(100)));
+    EventUtils
+        .triggerEvent(new InvocationFinishedEvent("fun1", InvocationType.PRODUCER,
+            TimeUnit.MILLISECONDS.toNanos(200), TimeUnit.MILLISECONDS.toNanos(300), 444, false));
+
+    EventUtils.triggerEvent(new InvocationStartedEvent("fun1", InvocationType.PRODUCER, System.nanoTime()));
+    EventUtils.triggerEvent(
+        new InvocationStartProcessingEvent("fun1", InvocationType.PRODUCER,
+            TimeUnit.MILLISECONDS.toNanos(100)));
+    EventUtils
+        .triggerEvent(new InvocationFinishedEvent("fun1", InvocationType.PRODUCER,
+            TimeUnit.MILLISECONDS.toNanos(200), TimeUnit.MILLISECONDS.toNanos(300), 555, false));
+
+    EventUtils.triggerEvent(new InvocationStartedEvent("fun1", InvocationType.PRODUCER, System.nanoTime()));
+    EventUtils.triggerEvent(
+        new InvocationStartProcessingEvent("fun1", InvocationType.PRODUCER,
+            TimeUnit.MILLISECONDS.toNanos(100)));
+    EventUtils
+        .triggerEvent(new InvocationFinishedEvent("fun1", InvocationType.PRODUCER,
+            TimeUnit.MILLISECONDS.toNanos(200), TimeUnit.MILLISECONDS.toNanos(300), 666, false));
+
+    //fun2 is a CONSUMER invocation call once and completed
+    EventUtils.triggerEvent(new InvocationStartedEvent("fun2", InvocationType.CONSUMER, System.nanoTime()));
+    EventUtils.triggerEvent(
+        new InvocationStartProcessingEvent("fun2", InvocationType.CONSUMER,
+            TimeUnit.MILLISECONDS.toNanos(100)));
+    EventUtils
+        .triggerEvent(new InvocationFinishedEvent("fun2", InvocationType.CONSUMER,
+            TimeUnit.MILLISECONDS.toNanos(200), TimeUnit.MILLISECONDS.toNanos(300), 200, true));
+
+    //sim lease one window time
+    Thread.sleep(1000);
+
+    return dataSource.getRegistryMetric(1000);
+  }
+}
diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerInvoke.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerInvoke.java
index ba8c126..020c926 100644
--- a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerInvoke.java
+++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerInvoke.java
@@ -134,7 +134,7 @@ public class HighwayServerInvoke {
     invocation.next(response -> {
       sendResponse(invocation.getContext(), response);
       endMetrics(invocation);
-      invocation.triggerFinishedEvent(response.isSuccessed());
+      invocation.triggerFinishedEvent(response.getStatusCode(), response.isSuccessed());
     });
   }
 

-- 
To stop receiving notification emails like this one, please contact
ningjiang@apache.org.