You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by wu...@apache.org on 2018/03/31 07:17:23 UTC

[incubator-servicecomb-java-chassis] 05/07: SCB-384 provide perf log publisher

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

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

commit b0133c1715a149438ccc84855b7f045ffb19302d
Author: wujimin <wu...@huawei.com>
AuthorDate: Fri Mar 23 17:30:58 2018 +0800

    SCB-384 provide perf log publisher
---
 .../metrics/core/publish/DefaultLogPublisher.java  | 170 ++++++++++++++++++
 ...rvicecomb.foundation.metrics.MetricsInitializer |   1 +
 .../core/publish/TestDefaultLogPublisher.java      | 191 +++++++++++++++++++++
 3 files changed, 362 insertions(+)

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 0000000..26a3815
--- /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/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 2bf2069..2933349 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 0000000..056dd25
--- /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());
+  }
+}

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