wujimin closed pull request #623: [SCB-384] Perf log publisher
URL: https://github.com/apache/incubator-servicecomb-java-chassis/pull/623
 
 
   

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

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

diff --git 
a/demo/perf/src/main/java/org/apache/servicecomb/demo/perf/PerfMain.java 
b/demo/perf/src/main/java/org/apache/servicecomb/demo/perf/PerfMain.java
index c06757764..871ccb430 100644
--- a/demo/perf/src/main/java/org/apache/servicecomb/demo/perf/PerfMain.java
+++ b/demo/perf/src/main/java/org/apache/servicecomb/demo/perf/PerfMain.java
@@ -19,8 +19,6 @@
 
 import java.util.Arrays;
 import java.util.List;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
 
 import org.apache.servicecomb.foundation.common.utils.BeanUtils;
 import org.apache.servicecomb.foundation.vertx.VertxUtils;
@@ -33,11 +31,6 @@ public static void main(String[] args) throws Exception {
     // redis
     RedisClientUtils.init(VertxUtils.getOrCreateVertxByName("transport", 
null));
 
-    // metrics
-    //DataSource dataSource = BeanUtils.getContext().getBean(Def.class);
-    PerfMetricsFilePublisher metricsLog = new PerfMetricsFilePublisher();
-    
Executors.newScheduledThreadPool(1).scheduleWithFixedDelay(metricsLog::onCycle, 
0, 1, TimeUnit.SECONDS);
-
     List<String> argList = Arrays.asList(args);
     if (argList.contains("-c")) {
       PerfConsumer consumer = 
BeanUtils.getContext().getBean(PerfConsumer.class);
diff --git 
a/demo/perf/src/main/java/org/apache/servicecomb/demo/perf/PerfMetricsFilePublisher.java
 
b/demo/perf/src/main/java/org/apache/servicecomb/demo/perf/PerfMetricsFilePublisher.java
deleted file mode 100644
index cbbd1d87c..000000000
--- 
a/demo/perf/src/main/java/org/apache/servicecomb/demo/perf/PerfMetricsFilePublisher.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.servicecomb.demo.perf;
-
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.servicecomb.foundation.metrics.MetricsConst;
-import org.apache.servicecomb.foundation.metrics.publish.MetricNode;
-import org.apache.servicecomb.foundation.metrics.publish.MetricsLoader;
-import org.apache.servicecomb.foundation.vertx.VertxUtils;
-import org.apache.servicecomb.metrics.core.MonitorManager;
-import org.apache.servicecomb.swagger.invocation.InvocationType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import io.vertx.core.impl.VertxImplEx;
-
-public class PerfMetricsFilePublisher {
-  private static final Logger LOGGER = 
LoggerFactory.getLogger(PerfMetricsFilePublisher.class);
-
-  public void onCycle() {
-    Map<String, Double> metrics = MonitorManager.getInstance().measure();
-    MetricsLoader loader = new MetricsLoader(metrics);
-
-    StringBuilder sb = new StringBuilder();
-    sb.append("\n");
-
-    collectSystemMetrics(loader, sb);
-    collectVertxMetrics(loader, sb);
-    collectMetrics(loader, sb);
-
-    LOGGER.info(sb.toString());
-  }
-
-  private void collectSystemMetrics(MetricsLoader loader, StringBuilder sb) {
-    double cpu = loader.getFirstMatchMetricValue(MetricsConst.JVM, 
MetricsConst.TAG_NAME, "cpuLoad");
-    // can not get cpu usage in windows, so skip this information
-    if (cpu >= 0) {
-      sb.append("cpu: ")
-          .append((long) cpu * Runtime.getRuntime().availableProcessors())
-          .append("%\n");
-    }
-  }
-
-  private void collectVertxMetrics(MetricsLoader loader, StringBuilder sb) {
-    sb.append("vertx:\n")
-        .append("  name       eventLoopContext-created\n");
-    for (Entry<String, VertxImplEx> entry : 
VertxUtils.getVertxMap().entrySet()) {
-      sb.append(String.format("  %-10s %-19d\n",
-          entry.getKey(),
-          entry.getValue().getEventLoopContextCreatedCount()));
-    }
-  }
-
-  private void collectMetrics(MetricsLoader loader, StringBuilder sb) {
-    if (loader.containsId(MetricsConst.SERVICECOMB_INVOCATION)) {
-      MetricNode treeNode = loader
-          .getMetricTree(MetricsConst.SERVICECOMB_INVOCATION, 
MetricsConst.TAG_ROLE, MetricsConst.TAG_OPERATION,
-              MetricsConst.TAG_STATUS);
-      if (treeNode != null && treeNode.getChildrenCount() != 0) {
-        MetricNode consumerNode = 
treeNode.getChildren(String.valueOf(InvocationType.CONSUMER).toLowerCase());
-        if (consumerNode != null) {
-          sb.append("consumer:\n");
-          sb.append("  tps     latency(ms) status  operation\n");
-          for (Entry<String, MetricNode> operationNode : 
consumerNode.getChildren()) {
-            for (Entry<String, MetricNode> statusNode : 
operationNode.getValue().getChildren()) {
-              sb.append(String.format("  %-7.0f %-11.3f %-9s %s\n",
-                  statusNode.getValue()
-                      .getFirstMatchMetricValue(MetricsConst.TAG_STAGE, 
MetricsConst.STAGE_TOTAL,
-                          MetricsConst.TAG_STATISTIC, "tps"),
-                  statusNode.getValue()
-                      .getFirstMatchMetricValue(TimeUnit.MILLISECONDS, 
MetricsConst.TAG_STAGE, MetricsConst.STAGE_TOTAL,
-                          MetricsConst.TAG_STATISTIC, "latency"),
-                  statusNode.getKey(), operationNode.getKey()));
-            }
-          }
-        }
-
-        MetricNode producerNode = 
treeNode.getChildren(String.valueOf(InvocationType.PRODUCER).toLowerCase());
-        if (producerNode != null) {
-          sb.append("producer:\n");
-          sb.append("  tps     latency(ms) queue(ms) execute(ms) status  
operation\n");
-          for (Entry<String, MetricNode> operationNode : 
producerNode.getChildren()) {
-            for (Entry<String, MetricNode> statusNode : 
operationNode.getValue().getChildren()) {
-              sb.append(String.format("  %-7.0f %-11.3f %-9.3f %-11.3f %-7s 
%s\n",
-                  statusNode.getValue()
-                      .getFirstMatchMetricValue(MetricsConst.TAG_STAGE, 
MetricsConst.STAGE_TOTAL,
-                          MetricsConst.TAG_STATISTIC, "tps"),
-                  statusNode.getValue()
-                      .getFirstMatchMetricValue(TimeUnit.MILLISECONDS, 
MetricsConst.TAG_STAGE, MetricsConst.STAGE_TOTAL,
-                          MetricsConst.TAG_STATISTIC, "latency"),
-                  statusNode.getValue()
-                      .getFirstMatchMetricValue(TimeUnit.MILLISECONDS, 
MetricsConst.TAG_STAGE, MetricsConst.STAGE_QUEUE,
-                          MetricsConst.TAG_STATISTIC, "latency"),
-                  statusNode.getValue()
-                      .getFirstMatchMetricValue(TimeUnit.MILLISECONDS, 
MetricsConst.TAG_STAGE,
-                          MetricsConst.STAGE_EXECUTION,
-                          MetricsConst.TAG_STATISTIC, "latency"),
-                  statusNode.getKey(), operationNode.getKey()));
-            }
-          }
-        }
-      }
-    }
-  }
-}
diff --git 
a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrapConfig.java
 
b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrapConfig.java
index 41baa95b6..eabe3e3e3 100644
--- 
a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrapConfig.java
+++ 
b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrapConfig.java
@@ -27,7 +27,7 @@
 
   public MetricsBootstrapConfig() {
     msPollInterval =
-        
DynamicPropertyFactory.getInstance().getIntProperty(METRICS_WINDOW_TIME, 
DEFAULT_METRICS_WINDOW_TIME).get();;
+        
DynamicPropertyFactory.getInstance().getIntProperty(METRICS_WINDOW_TIME, 
DEFAULT_METRICS_WINDOW_TIME).get();
   }
 
   public long getMsPollInterval() {
diff --git 
a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementNode.java
 
b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementNode.java
index 2d821134d..0518ebe19 100644
--- 
a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementNode.java
+++ 
b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementNode.java
@@ -82,4 +82,13 @@ public MeasurementNode addChild(String childName, 
Measurement measurement) {
   public void addMeasurement(Measurement measurement) {
     measurements.add(measurement);
   }
+
+  public double summary() {
+    double result = 0;
+    for (Measurement measurement : measurements) {
+      result += measurement.value();
+    }
+
+    return result;
+  }
 }
diff --git 
a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementNode.java
 
b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementNode.java
index cf55e5684..585fd9aec 100644
--- 
a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementNode.java
+++ 
b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementNode.java
@@ -25,6 +25,7 @@
 
 import com.netflix.spectator.api.Measurement;
 
+import mockit.Expectations;
 import mockit.Mocked;
 
 public class TestMeasurementNode {
@@ -79,4 +80,18 @@ public void getMeasurements(@Mocked Measurement measurement) 
{
 
     Assert.assertThat(node.getMeasurements(), Matchers.contains(measurement));
   }
+
+  @Test
+  public void summary(@Mocked Measurement measurement) {
+    new Expectations() {
+      {
+        measurement.value();
+        result = 10;
+      }
+    };
+    node.addMeasurement(measurement);
+    node.addMeasurement(measurement);
+
+    Assert.assertEquals(20, node.summary(), 0);
+  }
 }
diff --git a/foundations/foundation-test-scaffolding/pom.xml 
b/foundations/foundation-test-scaffolding/pom.xml
index abfe02492..06b05b994 100644
--- a/foundations/foundation-test-scaffolding/pom.xml
+++ b/foundations/foundation-test-scaffolding/pom.xml
@@ -48,5 +48,9 @@
       <groupId>com.netflix.spectator</groupId>
       <artifactId>spectator-reg-servo</artifactId>
     </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+    </dependency>
   </dependencies>
 </project>
diff --git 
a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java
 
b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java
new file mode 100644
index 000000000..33e580809
--- /dev/null
+++ 
b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicecomb.foundation.test.scaffolding.log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+
+public class LogCollector {
+  List<LoggingEvent> events = new ArrayList<>();
+
+  Appender appender = new AppenderSkeleton() {
+    @Override
+    public void append(LoggingEvent event) {
+      events.add(event);
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public boolean requiresLayout() {
+      return false;
+    }
+  };
+
+  public LogCollector() {
+    Logger.getRootLogger().addAppender(appender);
+  }
+
+  public List<LoggingEvent> getEvents() {
+    return events;
+  }
+
+  public void teardown() {
+    Logger.getRootLogger().removeAppender(appender);
+  }
+}
diff --git 
a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/DefaultLogPublisher.java
 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/DefaultLogPublisher.java
new file mode 100644
index 000000000..26a3815f5
--- /dev/null
+++ 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/DefaultLogPublisher.java
@@ -0,0 +1,170 @@
+/*
+ * 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.publish;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig;
+import org.apache.servicecomb.foundation.metrics.MetricsInitializer;
+import org.apache.servicecomb.foundation.metrics.PolledEvent;
+import org.apache.servicecomb.foundation.vertx.VertxUtils;
+import 
org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst;
+import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel;
+import 
org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerf;
+import 
org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroup;
+import 
org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups;
+import org.apache.servicecomb.metrics.core.publish.model.invocation.PerfInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
+import com.netflix.config.DynamicPropertyFactory;
+import com.netflix.spectator.api.CompositeRegistry;
+import com.netflix.spectator.api.Meter;
+
+import io.vertx.core.impl.VertxImplEx;
+
+public class DefaultLogPublisher implements MetricsInitializer {
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(DefaultLogPublisher.class);
+
+  public static final String ENABLED = 
"servicecomb.metrics.publisher.defaultLog.enabled";
+
+  @Override
+  public void init(CompositeRegistry globalRegistry, EventBus eventBus, 
MetricsBootstrapConfig config) {
+    if (!DynamicPropertyFactory.getInstance()
+        .getBooleanProperty(ENABLED, true)
+        .get()) {
+      return;
+    }
+
+    eventBus.register(this);
+  }
+
+  @Subscribe
+  public void onPolledEvent(PolledEvent event) {
+    try {
+      printLog(event.getMeters());
+    } catch (Throwable e) {
+      // make development easier
+      LOGGER.error("Failed to print perf log.", e);
+    }
+  }
+
+  protected void printLog(List<Meter> meters) {
+    StringBuilder sb = new StringBuilder();
+    sb.append("\n");
+
+    printVertxMetrics(sb);
+
+    PublishModelFactory factory = new PublishModelFactory(meters);
+    DefaultPublishModel model = factory.createDefaultPublishModel();
+    printConsumerLog(model, sb);
+    printProducerLog(model, sb);
+
+    LOGGER.info(sb.toString());
+  }
+
+  protected void printConsumerLog(DefaultPublishModel model, StringBuilder sb) 
{
+    OperationPerfGroups consumerPerf = 
model.getConsumer().getOperationPerfGroups();
+    if (consumerPerf == null) {
+      return;
+    }
+
+    sb.append("consumer:\n");
+    printConsumerPerfLog(consumerPerf, sb);
+  }
+
+  protected void printConsumerPerfLog(OperationPerfGroups consumerPerf, 
StringBuilder sb) {
+    sb.append("  tps     latency(ms) max-latency(ms) operation\n");
+    for (Map<String, OperationPerfGroup> statusMap : 
consumerPerf.getGroups().values()) {
+      for (OperationPerfGroup perfGroup : statusMap.values()) {
+        sb.append("  ")
+            .append(perfGroup.getTransport())
+            .append(".")
+            .append(perfGroup.getStatus())
+            .append(":\n");
+        for (OperationPerf operationPerf : perfGroup.getOperationPerfs()) {
+          printConsumerOperationPerf(operationPerf, sb);
+        }
+
+        printConsumerOperationPerf(perfGroup.getSummary(), sb);
+      }
+    }
+  }
+
+  protected void printConsumerOperationPerf(OperationPerf operationPerf, 
StringBuilder sb) {
+    PerfInfo stageTotal = 
operationPerf.findStage(MeterInvocationConst.STAGE_TOTAL);
+    sb.append(String.format("  %-7d %-11.3f %-15.3f %s\n",
+        stageTotal.getTps(),
+        stageTotal.calcMsLatency(),
+        stageTotal.getMsMaxLatency(),
+        operationPerf.getOperation()));
+  }
+
+  protected void printProducerLog(DefaultPublishModel model, StringBuilder sb) 
{
+    OperationPerfGroups producerPerf = 
model.getProducer().getOperationPerfGroups();
+    if (producerPerf == null) {
+      return;
+    }
+
+    sb.append("producer:\n");
+    sb.append(
+        "  tps     latency(ms) max-latency(ms) queue(ms) max-queue(ms) 
execute(ms) max-execute(ms) operation\n");
+    for (Map<String, OperationPerfGroup> statusMap : 
producerPerf.getGroups().values()) {
+      for (OperationPerfGroup perfGroup : statusMap.values()) {
+        sb.append("  ")
+            .append(perfGroup.getTransport())
+            .append(".")
+            .append(perfGroup.getStatus())
+            .append(":\n");
+        for (OperationPerf operationPerf : perfGroup.getOperationPerfs()) {
+          printProducerOperationPerf(operationPerf, sb);
+        }
+
+        printProducerOperationPerf(perfGroup.getSummary(), sb);
+      }
+    }
+  }
+
+  protected void printProducerOperationPerf(OperationPerf operationPerf, 
StringBuilder sb) {
+    PerfInfo stageTotal = 
operationPerf.findStage(MeterInvocationConst.STAGE_TOTAL);
+    PerfInfo stageQueue = 
operationPerf.findStage(MeterInvocationConst.STAGE_EXECUTOR_QUEUE);
+    PerfInfo stageExecution = 
operationPerf.findStage(MeterInvocationConst.STAGE_EXECUTION);
+    sb.append(String.format("  %-7d %-11.3f %-15.3f %-9.3f %-13.3f %-11.3f 
%-15.3f %s\n",
+        stageTotal.getTps(),
+        stageTotal.calcMsLatency(),
+        stageTotal.getMsMaxLatency(),
+        stageQueue.calcMsLatency(),
+        stageQueue.getMsMaxLatency(),
+        stageExecution.calcMsLatency(),
+        stageExecution.getMsMaxLatency(),
+        operationPerf.getOperation()));
+  }
+
+  protected void printVertxMetrics(StringBuilder sb) {
+    sb.append("vertx:\n")
+        .append("  name       eventLoopContext-created\n");
+    for (Entry<String, VertxImplEx> entry : 
VertxUtils.getVertxMap().entrySet()) {
+      sb.append(String.format("  %-10s %d\n",
+          entry.getKey(),
+          entry.getValue().getEventLoopContextCreatedCount()));
+    }
+  }
+}
diff --git 
a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishModelFactory.java
 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishModelFactory.java
new file mode 100644
index 000000000..7d03573da
--- /dev/null
+++ 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishModelFactory.java
@@ -0,0 +1,92 @@
+/*
+ * 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.publish;
+
+import java.util.List;
+
+import 
org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementGroupConfig;
+import 
org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNode;
+import 
org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementTree;
+import 
org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst;
+import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel;
+import 
org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups;
+import org.apache.servicecomb.swagger.invocation.InvocationType;
+
+import com.netflix.spectator.api.Meter;
+
+public class PublishModelFactory {
+  private MeasurementTree tree;
+
+  public PublishModelFactory(List<Meter> meters) {
+    tree = createMeasurementTree(meters);
+  }
+
+  protected MeasurementTree createMeasurementTree(List<Meter> meters) {
+    MeasurementGroupConfig groupConfig = createMeasurementGroupConfig();
+
+    MeasurementTree tree = new MeasurementTree();
+    tree.from(meters.iterator(), groupConfig);
+    return tree;
+  }
+
+  protected MeasurementGroupConfig createMeasurementGroupConfig() {
+    MeasurementGroupConfig groupConfig = new MeasurementGroupConfig();
+    groupConfig.addGroup(MeterInvocationConst.INVOCATION_NAME,
+        MeterInvocationConst.TAG_ROLE,
+        MeterInvocationConst.TAG_TRANSPORT,
+        MeterInvocationConst.TAG_OPERATION,
+        MeterInvocationConst.TAG_STATUS,
+        MeterInvocationConst.TAG_STAGE,
+        MeterInvocationConst.TAG_STATISTIC);
+    return groupConfig;
+  }
+
+  protected OperationPerfGroups generateOperationPerfGroups(MeasurementTree 
tree, String invocationTypeName) {
+    MeasurementNode node = 
tree.findChild(MeterInvocationConst.INVOCATION_NAME, invocationTypeName);
+    if (node == null) {
+      return null;
+    }
+
+    OperationPerfGroups groups = new OperationPerfGroups();
+
+    // group by transport
+    for (MeasurementNode transportNode : node.getChildren().values()) {
+      // group by operation
+      for (MeasurementNode operationNode : 
transportNode.getChildren().values()) {
+        // group by status
+        for (MeasurementNode statusNode : 
operationNode.getChildren().values()) {
+          PublishUtils.addOperationPerfGroups(groups, transportNode.getName(), 
operationNode.getName(), statusNode);
+        }
+      }
+    }
+
+    return groups;
+  }
+
+  public DefaultPublishModel createDefaultPublishModel() {
+    DefaultPublishModel model = new DefaultPublishModel();
+
+    model
+        .getConsumer()
+        .setOperationPerfGroups(generateOperationPerfGroups(tree, 
InvocationType.CONSUMER.name()));
+    model
+        .getProducer()
+        .setOperationPerfGroups(generateOperationPerfGroups(tree, 
InvocationType.PRODUCER.name()));
+
+    return model;
+  }
+}
diff --git 
a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishUtils.java
 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishUtils.java
new file mode 100644
index 000000000..16680c7a1
--- /dev/null
+++ 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishUtils.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicecomb.metrics.core.publish;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import 
org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNode;
+import 
org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerf;
+import 
org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroup;
+import 
org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups;
+import org.apache.servicecomb.metrics.core.publish.model.invocation.PerfInfo;
+
+import com.netflix.spectator.api.Statistic;
+
+public final class PublishUtils {
+  private PublishUtils() {
+  }
+
+  public static PerfInfo createPerfInfo(MeasurementNode stageNode) {
+    PerfInfo perfInfo = new PerfInfo();
+
+    perfInfo.setTps((int) 
stageNode.findChild(Statistic.count.name()).summary());
+    
perfInfo.setMsTotalTime(stageNode.findChild(Statistic.totalTime.name()).summary()
 * 1000);
+    // when UT with DefaultRegistry, there is no max value
+    MeasurementNode maxNode = stageNode.findChild(Statistic.max.name());
+    if (maxNode != null) {
+      perfInfo.setMsMaxLatency(maxNode.summary() * 1000);
+    }
+
+    return perfInfo;
+  }
+
+  public static OperationPerf createOperationPerf(String operation, 
MeasurementNode statusNode) {
+    OperationPerf operationPerf = new OperationPerf();
+
+    operationPerf.setOperation(operation);
+    for (MeasurementNode stageNode : statusNode.getChildren().values()) {
+      PerfInfo perfInfo = createPerfInfo(stageNode);
+      operationPerf.getStages().put(stageNode.getName(), perfInfo);
+    }
+
+    return operationPerf;
+  }
+
+  public static void addOperationPerfGroups(OperationPerfGroups 
operationPerfGroups, String transport, String operation,
+      MeasurementNode statusNode) {
+    Map<String, OperationPerfGroup> statusMap = operationPerfGroups
+        .getGroups()
+        .computeIfAbsent(transport, tn -> {
+          return new HashMap<>();
+        });
+    OperationPerfGroup group = statusMap.computeIfAbsent(statusNode.getName(), 
status -> {
+      return new OperationPerfGroup(transport, status);
+    });
+
+    OperationPerf operationPerf = createOperationPerf(operation, statusNode);
+    group.addOperationPerf(operationPerf);
+  }
+}
diff --git 
a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ConsumerPublishModel.java
 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ConsumerPublishModel.java
new file mode 100644
index 000000000..22e40bdbe
--- /dev/null
+++ 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ConsumerPublishModel.java
@@ -0,0 +1,31 @@
+/*
+ * 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.publish.model;
+
+import 
org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups;
+
+public class ConsumerPublishModel {
+  private OperationPerfGroups operationPerfGroups;
+
+  public OperationPerfGroups getOperationPerfGroups() {
+    return operationPerfGroups;
+  }
+
+  public void setOperationPerfGroups(OperationPerfGroups operationPerfGroups) {
+    this.operationPerfGroups = operationPerfGroups;
+  }
+}
diff --git 
a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/DefaultPublishModel.java
 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/DefaultPublishModel.java
new file mode 100644
index 000000000..8258425ca
--- /dev/null
+++ 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/DefaultPublishModel.java
@@ -0,0 +1,31 @@
+/*
+ * 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.publish.model;
+
+public class DefaultPublishModel {
+  private ConsumerPublishModel consumer = new ConsumerPublishModel();
+
+  private ProducerPublishModel producer = new ProducerPublishModel();
+
+  public ConsumerPublishModel getConsumer() {
+    return consumer;
+  }
+
+  public ProducerPublishModel getProducer() {
+    return producer;
+  }
+}
diff --git 
a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ProducerPublishModel.java
 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ProducerPublishModel.java
new file mode 100644
index 000000000..d02c98641
--- /dev/null
+++ 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/ProducerPublishModel.java
@@ -0,0 +1,31 @@
+/*
+ * 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.publish.model;
+
+import 
org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups;
+
+public class ProducerPublishModel {
+  private OperationPerfGroups operationPerfGroups;
+
+  public OperationPerfGroups getOperationPerfGroups() {
+    return operationPerfGroups;
+  }
+
+  public void setOperationPerfGroups(OperationPerfGroups operationPerfGroups) {
+    this.operationPerfGroups = operationPerfGroups;
+  }
+}
diff --git 
a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerf.java
 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerf.java
new file mode 100644
index 000000000..4679d9c0d
--- /dev/null
+++ 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerf.java
@@ -0,0 +1,56 @@
+/*
+ * 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.publish.model.invocation;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class OperationPerf {
+  private String operation;
+
+  private Map<String, PerfInfo> stages = new HashMap<>();
+
+  public String getOperation() {
+    return operation;
+  }
+
+  public void setOperation(String operation) {
+    this.operation = operation;
+  }
+
+  public Map<String, PerfInfo> getStages() {
+    return stages;
+  }
+
+  public void setStages(Map<String, PerfInfo> stages) {
+    this.stages = stages;
+  }
+
+  public PerfInfo findStage(String stage) {
+    return stages.get(stage);
+  }
+
+  public void add(OperationPerf operationPerf) {
+    for (Entry<String, PerfInfo> entry : operationPerf.stages.entrySet()) {
+      PerfInfo perfInfo = stages.computeIfAbsent(entry.getKey(), n -> {
+        return new PerfInfo();
+      });
+      perfInfo.add(entry.getValue());
+    }
+  }
+}
diff --git 
a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerfGroup.java
 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerfGroup.java
new file mode 100644
index 000000000..01e0fb8dc
--- /dev/null
+++ 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerfGroup.java
@@ -0,0 +1,61 @@
+/*
+ * 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.publish.model.invocation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class OperationPerfGroup {
+  private String transport;
+
+  private String status;
+
+  private List<OperationPerf> operationPerfs = new ArrayList<>();
+
+  private OperationPerf summary;
+
+  public OperationPerfGroup(String transport, String status) {
+    this.transport = transport;
+    this.status = status;
+  }
+
+  public String getTransport() {
+    return transport;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public List<OperationPerf> getOperationPerfs() {
+    return operationPerfs;
+  }
+
+  public OperationPerf getSummary() {
+    return summary;
+  }
+
+  public void addOperationPerf(OperationPerf operationPerf) {
+    operationPerfs.add(operationPerf);
+
+    if (summary == null) {
+      summary = new OperationPerf();
+      summary.setOperation("");
+    }
+    summary.add(operationPerf);
+  }
+}
diff --git 
a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerfGroups.java
 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerfGroups.java
new file mode 100644
index 000000000..addef3fa3
--- /dev/null
+++ 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/OperationPerfGroups.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicecomb.metrics.core.publish.model.invocation;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class OperationPerfGroups {
+  // first key:  transport
+  // second key: statusCode
+  private Map<String, Map<String, OperationPerfGroup>> groups = new 
HashMap<>();
+
+  public Map<String, Map<String, OperationPerfGroup>> getGroups() {
+    return groups;
+  }
+
+  public void setGroups(Map<String, Map<String, OperationPerfGroup>> groups) {
+    this.groups = groups;
+  }
+}
diff --git 
a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/PerfInfo.java
 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/PerfInfo.java
new file mode 100644
index 000000000..6ab8b4d1e
--- /dev/null
+++ 
b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/PerfInfo.java
@@ -0,0 +1,68 @@
+/*
+ * 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.publish.model.invocation;
+
+public class PerfInfo {
+  private int tps;
+
+  private double msTotalTime;
+
+  private double msMaxLatency;
+
+  public int getTps() {
+    return tps;
+  }
+
+  public void setTps(int tps) {
+    this.tps = tps;
+  }
+
+  public double getMsTotalTime() {
+    return msTotalTime;
+  }
+
+  public void setMsTotalTime(double msTotalTime) {
+    this.msTotalTime = msTotalTime;
+  }
+
+  public double getMsMaxLatency() {
+    return msMaxLatency;
+  }
+
+  public void setMsMaxLatency(double msMaxLatency) {
+    this.msMaxLatency = msMaxLatency;
+  }
+
+  public void add(PerfInfo other) {
+    tps += other.tps;
+    msTotalTime += other.msTotalTime;
+    if (msMaxLatency < other.msMaxLatency) {
+      msMaxLatency = other.msMaxLatency;
+    }
+  }
+
+  public double calcMsLatency() {
+    return (tps != 0) ? msTotalTime / tps : 0;
+  }
+
+  @Override
+  public String toString() {
+    return "PerfInfo [tps=" + tps + ", msTotalTime=" + msTotalTime + ", 
msLatency=" + calcMsLatency()
+        + ", msMaxLatency="
+        + msMaxLatency + "]";
+  }
+}
diff --git 
a/metrics/metrics-core/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.metrics.MetricsInitializer
 
b/metrics/metrics-core/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.metrics.MetricsInitializer
index 2bf2069c6..293334974 100644
--- 
a/metrics/metrics-core/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.metrics.MetricsInitializer
+++ 
b/metrics/metrics-core/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.metrics.MetricsInitializer
@@ -16,3 +16,4 @@
 #
 
 org.apache.servicecomb.metrics.core.DefaultMetricsInitializer
+org.apache.servicecomb.metrics.core.publish.DefaultLogPublisher
\ No newline at end of file
diff --git 
a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestDefaultLogPublisher.java
 
b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestDefaultLogPublisher.java
new file mode 100644
index 000000000..056dd256b
--- /dev/null
+++ 
b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestDefaultLogPublisher.java
@@ -0,0 +1,191 @@
+/*
+ * 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.publish;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.core.Response.Status;
+import javax.xml.ws.Holder;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.servicecomb.core.Const;
+import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig;
+import org.apache.servicecomb.foundation.metrics.PolledEvent;
+import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
+import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector;
+import org.apache.servicecomb.foundation.vertx.VertxUtils;
+import 
org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst;
+import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel;
+import 
org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerf;
+import 
org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroup;
+import 
org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups;
+import org.apache.servicecomb.metrics.core.publish.model.invocation.PerfInfo;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.eventbus.EventBus;
+import com.netflix.spectator.api.CompositeRegistry;
+
+import io.vertx.core.impl.VertxImplEx;
+import mockit.Expectations;
+import mockit.Mock;
+import mockit.MockUp;
+import mockit.Mocked;
+
+public class TestDefaultLogPublisher {
+  CompositeRegistry globalRegistry = null;
+
+  EventBus eventBus = new EventBus();
+
+  DefaultLogPublisher publisher = new DefaultLogPublisher();
+
+  LogCollector collector = new LogCollector();
+
+  @Before
+  public void setup() {
+
+  }
+
+  @After
+  public void teardown() {
+    collector.teardown();
+    ArchaiusUtils.resetConfig();
+  }
+
+  @Test
+  public void init_enabled_default() {
+    Holder<Boolean> registered = new Holder<>();
+    new MockUp<EventBus>(eventBus) {
+      @Mock
+      void register(Object object) {
+        registered.value = true;
+      }
+    };
+
+    publisher.init(globalRegistry, eventBus, new MetricsBootstrapConfig());
+    Assert.assertTrue(registered.value);
+  }
+
+  @Test
+  public void init_enabled_true() {
+    Holder<Boolean> registered = new Holder<>();
+    new MockUp<EventBus>(eventBus) {
+      @Mock
+      void register(Object object) {
+        registered.value = true;
+      }
+    };
+
+    ArchaiusUtils.setProperty(DefaultLogPublisher.ENABLED, true);
+
+    publisher.init(globalRegistry, eventBus, new MetricsBootstrapConfig());
+    Assert.assertTrue(registered.value);
+  }
+
+  @Test
+  public void init_enabled_false() {
+    Holder<Boolean> registered = new Holder<>();
+    new MockUp<EventBus>(eventBus) {
+      @Mock
+      void register(Object object) {
+        registered.value = true;
+      }
+    };
+
+    ArchaiusUtils.setProperty(DefaultLogPublisher.ENABLED, false);
+
+    publisher.init(globalRegistry, eventBus, new MetricsBootstrapConfig());
+    Assert.assertNull(registered.value);
+  }
+
+  @Test
+  public void onPolledEvent_failed() {
+    publisher.onPolledEvent(null);
+
+    LoggingEvent event = collector.getEvents().get(0);
+    Assert.assertEquals("Failed to print perf log.", event.getMessage());
+    Assert.assertEquals(NullPointerException.class, 
event.getThrowableInformation().getThrowable().getClass());
+  }
+
+  @Test
+  public void onPolledEvent(@Mocked VertxImplEx vertxImplEx) {
+    new Expectations(VertxUtils.class) {
+      {
+        VertxUtils.getVertxMap();
+        result = Collections.singletonMap("v", vertxImplEx);
+        vertxImplEx.getEventLoopContextCreatedCount();
+        result = 1;
+      }
+    };
+
+    DefaultPublishModel model = new DefaultPublishModel();
+
+    PerfInfo perfTotal = new PerfInfo();
+    perfTotal.setTps(10);
+    perfTotal.setMsTotalTime(100);
+
+    OperationPerf operationPerf = new OperationPerf();
+    operationPerf.setOperation("op");
+    operationPerf.getStages().put(MeterInvocationConst.STAGE_TOTAL, perfTotal);
+    operationPerf.getStages().put(MeterInvocationConst.STAGE_EXECUTOR_QUEUE, 
perfTotal);
+    operationPerf.getStages().put(MeterInvocationConst.STAGE_EXECUTION, 
perfTotal);
+
+    OperationPerfGroup operationPerfGroup = new 
OperationPerfGroup(Const.RESTFUL, Status.OK.name());
+    operationPerfGroup.addOperationPerf(operationPerf);
+
+    OperationPerfGroups operationPerfGroups = new OperationPerfGroups();
+    operationPerfGroups.getGroups().put(operationPerfGroup.getTransport(),
+        Collections.singletonMap(operationPerfGroup.getStatus(), 
operationPerfGroup));
+    model.getConsumer().setOperationPerfGroups(operationPerfGroups);
+    model.getProducer().setOperationPerfGroups(operationPerfGroups);
+
+    new MockUp<PublishModelFactory>() {
+      @Mock
+      DefaultPublishModel createDefaultPublishModel() {
+        return model;
+      }
+    };
+
+    publisher.onPolledEvent(new PolledEvent(Collections.emptyList()));
+
+    List<LoggingEvent> events = collector.getEvents().stream().filter(e -> {
+      return DefaultLogPublisher.class.getName().equals(e.getLoggerName());
+    }).collect(Collectors.toList());
+
+    LoggingEvent event = events.get(0);
+    Assert.assertEquals("\n" +
+        "vertx:\n" +
+        "  name       eventLoopContext-created\n" +
+        "  v          1\n" +
+        "consumer:\n" +
+        "  tps     latency(ms) max-latency(ms) operation\n" +
+        "  rest.OK:\n" +
+        "  10      10.000      0.000           op\n" +
+        "  10      10.000      0.000           \n" +
+        "producer:\n" +
+        "  tps     latency(ms) max-latency(ms) queue(ms) max-queue(ms) 
execute(ms) max-execute(ms) operation\n" +
+        "  rest.OK:\n" +
+        "  10      10.000      0.000           10.000    0.000         10.000  
    0.000           op\n" +
+        "  10      10.000      0.000           10.000    0.000         10.000  
    0.000           \n" +
+        "",
+        event.getMessage());
+  }
+}
diff --git 
a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishModelFactory.java
 
b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishModelFactory.java
new file mode 100644
index 000000000..4135cf0d0
--- /dev/null
+++ 
b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishModelFactory.java
@@ -0,0 +1,131 @@
+/*
+ * 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.publish;
+
+import java.util.List;
+
+import org.apache.servicecomb.core.Const;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.foundation.common.utils.JsonUtils;
+import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig;
+import org.apache.servicecomb.metrics.core.DefaultMetricsInitializer;
+import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel;
+import org.apache.servicecomb.swagger.invocation.InvocationType;
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.common.collect.Lists;
+import com.google.common.eventbus.EventBus;
+import com.netflix.spectator.api.CompositeRegistry;
+import com.netflix.spectator.api.DefaultRegistry;
+import com.netflix.spectator.api.ManualClock;
+import com.netflix.spectator.api.Meter;
+import com.netflix.spectator.api.Registry;
+import com.netflix.spectator.api.SpectatorUtils;
+
+import mockit.Expectations;
+import mockit.Mock;
+import mockit.MockUp;
+import mockit.Mocked;
+
+public class TestPublishModelFactory {
+  @Mocked
+  Invocation invocation;
+
+  @Mocked
+  Response response;
+
+  InvocationType invocationType;
+
+  @Test
+  public void createDefaultPublishModel() throws JsonProcessingException {
+    Registry registry = prepareRegistry();
+    List<Meter> meters = Lists.newArrayList(registry);
+    PublishModelFactory factory = new PublishModelFactory(meters);
+    DefaultPublishModel model = factory.createDefaultPublishModel();
+
+    Assert.assertEquals(
+        
"{\"consumer\":{\"operationPerfGroups\":{\"groups\":{\"rest\":{\"200\":{\"transport\":\"rest\",\"status\":\"200\",\"operationPerfs\":[{\"operation\":\"m.s.o\",\"stages\":{\"total\":{\"tps\":1,\"msTotalTime\":10000.0,\"msMaxLatency\":0.0}}}],\"summary\":{\"operation\":\"\",\"stages\":{\"total\":{\"tps\":1,\"msTotalTime\":10000.0,\"msMaxLatency\":0.0}}}}}}}},\"producer\":{\"operationPerfGroups\":{\"groups\":{\"rest\":{\"200\":{\"transport\":\"rest\",\"status\":\"200\",\"operationPerfs\":[{\"operation\":\"m.s.o\",\"stages\":{\"execution\":{\"tps\":1,\"msTotalTime\":5000.0,\"msMaxLatency\":0.0},\"total\":{\"tps\":1,\"msTotalTime\":10000.0,\"msMaxLatency\":0.0},\"queue\":{\"tps\":1,\"msTotalTime\":5000.0,\"msMaxLatency\":0.0}}}],\"summary\":{\"operation\":\"\",\"stages\":{\"execution\":{\"tps\":1,\"msTotalTime\":5000.0,\"msMaxLatency\":0.0},\"total\":{\"tps\":1,\"msTotalTime\":10000.0,\"msMaxLatency\":0.0},\"queue\":{\"tps\":1,\"msTotalTime\":5000.0,\"msMaxLatency\":0.0}}}}}}}}}",
+        JsonUtils.writeValueAsString(model));
+  }
+
+  protected Registry prepareRegistry() {
+    CompositeRegistry globalRegistry = 
SpectatorUtils.createCompositeRegistry(null);
+    Registry registry = new DefaultRegistry(new ManualClock());
+    EventBus eventBus = new EventBus();
+
+    DefaultMetricsInitializer metricsInitializer = new 
DefaultMetricsInitializer() {
+      protected Registry createRegistry(MetricsBootstrapConfig config) {
+        return registry;
+      };
+    };
+    metricsInitializer.init(globalRegistry, eventBus, new 
MetricsBootstrapConfig());
+
+    new MockUp<System>() {
+      @Mock
+      long nanoTime() {
+        return 10;
+      }
+    };
+
+    invocationType = InvocationType.CONSUMER;
+    new MockUp<Invocation>() {
+      @Mock
+      InvocationType getInvocationType() {
+        return invocationType;
+      }
+
+      @Mock
+      boolean isConsumer() {
+        return InvocationType.CONSUMER.equals(invocationType);
+      }
+
+      @Mock
+      String getRealTransportName() {
+        return Const.RESTFUL;
+      }
+
+      @Mock
+      String getMicroserviceQualifiedName() {
+        return "m.s.o";
+      }
+
+      @Mock
+      long getStartExecutionTime() {
+        return 5;
+      }
+    };
+
+
+    new Expectations() {
+      {
+        response.getStatusCode();
+        result = 200;
+      }
+    };
+    InvocationFinishEvent finishEvent = new InvocationFinishEvent(invocation, 
response);
+    eventBus.post(finishEvent);
+
+    invocationType = InvocationType.PRODUCER;
+    eventBus.post(finishEvent);
+
+    return registry;
+  }
+}
diff --git 
a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishUtils.java
 
b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishUtils.java
new file mode 100644
index 000000000..29ddcc611
--- /dev/null
+++ 
b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishUtils.java
@@ -0,0 +1,75 @@
+/*
+ * 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.publish;
+
+import java.util.Map;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.servicecomb.core.Const;
+import 
org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNode;
+import 
org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst;
+import org.apache.servicecomb.metrics.core.publish.PublishUtils;
+import 
org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerf;
+import 
org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroup;
+import 
org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups;
+import org.apache.servicecomb.metrics.core.publish.model.invocation.PerfInfo;
+import org.apache.servicecomb.metrics.core.publish.model.invocation.Utils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestPublishUtils {
+  String op = "op";
+
+  @Test
+  public void createPerfInfo() {
+    MeasurementNode stageNode = 
Utils.createStageNode(MeterInvocationConst.STAGE_TOTAL, 10, 10, 100);
+
+    PerfInfo perf = PublishUtils.createPerfInfo(stageNode);
+
+    Assert.assertEquals(10, perf.getTps());
+    Assert.assertEquals(1000, perf.calcMsLatency(), 0);
+    Assert.assertEquals(100000, perf.getMsMaxLatency(), 0);
+  }
+
+  @Test
+  public void createOperationPerf() {
+    OperationPerf opPerf = Utils.createOperationPerf(op);
+
+    PerfInfo perfInfo = opPerf.findStage(MeterInvocationConst.STAGE_TOTAL);
+    Assert.assertEquals(10, perfInfo.getTps());
+    Assert.assertEquals(1000, perfInfo.calcMsLatency(), 0);
+    Assert.assertEquals(100000, perfInfo.getMsMaxLatency(), 0);
+  }
+
+  @Test
+  public void addOperationPerfGroups() {
+    OperationPerfGroups groups = new OperationPerfGroups();
+    PublishUtils.addOperationPerfGroups(groups,
+        Const.RESTFUL,
+        op,
+        Utils.createStatusNode(Status.OK.name(), Utils.totalStageNode));
+
+    Map<String, OperationPerfGroup> statusMap = 
groups.getGroups().get(Const.RESTFUL);
+    OperationPerfGroup group = statusMap.get(Status.OK.name());
+
+    PerfInfo perfInfo = 
group.getSummary().findStage(MeterInvocationConst.STAGE_TOTAL);
+    Assert.assertEquals(10, perfInfo.getTps());
+    Assert.assertEquals(1000, perfInfo.calcMsLatency(), 0);
+    Assert.assertEquals(100000, perfInfo.getMsMaxLatency(), 0);
+  }
+}
diff --git 
a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerf.java
 
b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerf.java
new file mode 100644
index 000000000..ef7af2377
--- /dev/null
+++ 
b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerf.java
@@ -0,0 +1,47 @@
+/*
+ * 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.publish.model.invocation;
+
+import 
org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestOperationPerf {
+  String op = "op";
+
+  OperationPerf opPerf = new OperationPerf();
+
+  @Test
+  public void add() {
+    Assert.assertTrue(opPerf.getStages().isEmpty());
+
+    OperationPerf otherOpPerf = Utils.createOperationPerf(op);
+    opPerf.add(otherOpPerf);
+
+    Assert.assertEquals(op, otherOpPerf.getOperation());
+
+    PerfInfo perfInfo = opPerf.findStage(MeterInvocationConst.STAGE_TOTAL);
+    Assert.assertEquals(10, perfInfo.getTps());
+    Assert.assertEquals(1000, perfInfo.calcMsLatency(), 0);
+    Assert.assertEquals(100000, perfInfo.getMsMaxLatency(), 0);
+
+    perfInfo = opPerf.findStage(MeterInvocationConst.STAGE_EXECUTION);
+    Assert.assertEquals(10, perfInfo.getTps());
+    Assert.assertEquals(1000, perfInfo.calcMsLatency(), 0);
+    Assert.assertEquals(100000, perfInfo.getMsMaxLatency(), 0);
+  }
+}
diff --git 
a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerfGroup.java
 
b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerfGroup.java
new file mode 100644
index 000000000..f3655fa30
--- /dev/null
+++ 
b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerfGroup.java
@@ -0,0 +1,60 @@
+/*
+ * 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.publish.model.invocation;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.servicecomb.core.Const;
+import 
org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestOperationPerfGroup {
+  String op = "op";
+
+  OperationPerfGroup group = new OperationPerfGroup(Const.RESTFUL, 
Status.OK.name());
+
+  @Test
+  public void construct() {
+    Assert.assertEquals(Const.RESTFUL, group.getTransport());
+    Assert.assertEquals(Status.OK.name(), group.getStatus());
+    Assert.assertTrue(group.getOperationPerfs().isEmpty());
+    Assert.assertNull(group.getSummary());
+  }
+
+  @Test
+  public void addOperationPerf() {
+    OperationPerf opPerf = Utils.createOperationPerf(op);
+    group.addOperationPerf(opPerf);
+    group.addOperationPerf(opPerf);
+
+    Assert.assertThat(group.getOperationPerfs(), Matchers.contains(opPerf, 
opPerf));
+
+    OperationPerf summary = group.getSummary();
+
+    PerfInfo perfInfo = summary.findStage(MeterInvocationConst.STAGE_TOTAL);
+    Assert.assertEquals(20, perfInfo.getTps());
+    Assert.assertEquals(1000, perfInfo.calcMsLatency(), 0);
+    Assert.assertEquals(100000, perfInfo.getMsMaxLatency(), 0);
+
+    perfInfo = summary.findStage(MeterInvocationConst.STAGE_EXECUTION);
+    Assert.assertEquals(20, perfInfo.getTps());
+    Assert.assertEquals(1000, perfInfo.calcMsLatency(), 0);
+    Assert.assertEquals(100000, perfInfo.getMsMaxLatency(), 0);
+  }
+}
diff --git 
a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestPerfInfo.java
 
b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestPerfInfo.java
new file mode 100644
index 000000000..ce147e652
--- /dev/null
+++ 
b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestPerfInfo.java
@@ -0,0 +1,85 @@
+/*
+ * 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.publish.model.invocation;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestPerfInfo {
+  @Test
+  public void construct() {
+    PerfInfo perf = new PerfInfo();
+
+    Assert.assertEquals(0, perf.getTps());
+    Assert.assertEquals(0, perf.getMsTotalTime(), 0);
+    Assert.assertEquals(0, perf.getMsMaxLatency(), 0);
+    Assert.assertEquals(0, perf.calcMsLatency(), 0);
+  }
+
+  @Test
+  public void add_changeMax() {
+    PerfInfo sum = new PerfInfo();
+
+    PerfInfo other = new PerfInfo();
+    other.setTps(10);
+    other.setMsTotalTime(10);
+    other.setMsMaxLatency(100);
+    sum.add(other);
+
+    other = new PerfInfo();
+    other.setTps(20);
+    other.setMsTotalTime(20);
+    other.setMsMaxLatency(200);
+    sum.add(other);
+
+    Assert.assertEquals(30, sum.getTps());
+    Assert.assertEquals(30, sum.getMsTotalTime(), 0);
+    Assert.assertEquals(200, sum.getMsMaxLatency(), 0);
+    Assert.assertEquals(1.0, sum.calcMsLatency(), 0);
+  }
+
+  @Test
+  public void add_notChangeMax() {
+    PerfInfo sum = new PerfInfo();
+
+    PerfInfo other = new PerfInfo();
+    other.setTps(10);
+    other.setMsTotalTime(10);
+    other.setMsMaxLatency(100);
+    sum.add(other);
+
+    other = new PerfInfo();
+    other.setTps(20);
+    other.setMsTotalTime(20);
+    other.setMsMaxLatency(50);
+    sum.add(other);
+
+    Assert.assertEquals(30, sum.getTps());
+    Assert.assertEquals(1.0, sum.calcMsLatency(), 0);
+    Assert.assertEquals(100, sum.getMsMaxLatency(), 0);
+  }
+
+  @Test
+  public void testToString() {
+    PerfInfo perf = new PerfInfo();
+    perf.setTps(10);
+    perf.setMsTotalTime(10);
+    perf.setMsMaxLatency(100);
+
+    Assert.assertEquals("PerfInfo [tps=10, msTotalTime=10.0, msLatency=1.0, 
msMaxLatency=100.0]", perf.toString());
+  }
+}
diff --git 
a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/Utils.java
 
b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/Utils.java
new file mode 100644
index 000000000..ec2f7704f
--- /dev/null
+++ 
b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/Utils.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicecomb.metrics.core.publish.model.invocation;
+
+import java.util.HashMap;
+
+import javax.ws.rs.core.Response.Status;
+
+import 
org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNode;
+import 
org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst;
+import org.apache.servicecomb.metrics.core.publish.PublishUtils;
+
+import com.netflix.spectator.api.DefaultRegistry;
+import com.netflix.spectator.api.Id;
+import com.netflix.spectator.api.Measurement;
+import com.netflix.spectator.api.Registry;
+import com.netflix.spectator.api.Statistic;
+
+public class Utils {
+  static Registry registry = new DefaultRegistry();
+
+  public static MeasurementNode totalStageNode = 
Utils.createStageNode(MeterInvocationConst.STAGE_TOTAL, 10, 10, 100);
+
+  public static MeasurementNode executeStageNode =
+      Utils.createStageNode(MeterInvocationConst.STAGE_EXECUTION, 10, 10, 100);
+
+  public static MeasurementNode createStageNode(String stage,
+      double count,
+      double totalTime,
+      double max) {
+    Id id = registry.createId("id").withTag(Statistic.count);
+    Measurement countMeasurement = new 
Measurement(id.withTag(Statistic.count), 0, count);
+    Measurement totalTimeMeasurement = new 
Measurement(id.withTag(Statistic.totalTime), 0, totalTime);
+    Measurement maxMeasurement = new Measurement(id.withTag(Statistic.max), 0, 
max);
+
+    MeasurementNode stageNode = new MeasurementNode(stage, null);
+    stageNode.addChild(Statistic.count.name(), countMeasurement);
+    stageNode.addChild(Statistic.totalTime.name(), totalTimeMeasurement);
+    stageNode.addChild(Statistic.max.name(), maxMeasurement);
+
+    return stageNode;
+  }
+
+  public static MeasurementNode createStatusNode(String status, 
MeasurementNode... stageNodes) {
+    MeasurementNode statusNode = new MeasurementNode(status, new HashMap<>());
+
+    for (MeasurementNode stageNode : stageNodes) {
+      statusNode.getChildren().put(stageNode.getName(), stageNode);
+    }
+    return statusNode;
+  }
+
+  public static OperationPerf createOperationPerf(String op) {
+    MeasurementNode statusNode = createStatusNode(Status.OK.name(), 
totalStageNode, executeStageNode);
+    return PublishUtils.createOperationPerf(op, statusNode);
+  }
+}


 

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


With regards,
Apache Git Services

Reply via email to