You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2018/03/15 11:08:49 UTC

[incubator-servicecomb-java-chassis] 02/03: SCB-369 extract informations from spectator measurement, just like sql: select * from meters group by ......, but output is a tree not a table

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

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

commit b081e2902528d89c7f14bb1e09a6aed7c0bd54b6
Author: wujimin <wu...@huawei.com>
AuthorDate: Wed Mar 7 16:57:05 2018 +0800

    SCB-369 extract informations from spectator measurement, just like sql: select * from meters group by ......, but output is a tree not a table
---
 .../publish/spectator/MeasurementGroupConfig.java  | 48 ++++++++++++
 .../metrics/publish/spectator/MeasurementNode.java | 85 ++++++++++++++++++++++
 .../metrics/publish/spectator/MeasurementTree.java | 67 +++++++++++++++++
 .../spectator/TestMeasurementGroupConfig.java      | 72 ++++++++++++++++++
 .../publish/spectator/TestMeasurementNode.java     | 82 +++++++++++++++++++++
 .../publish/spectator/TestMeasurementTree.java     | 84 +++++++++++++++++++++
 6 files changed, 438 insertions(+)

diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementGroupConfig.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementGroupConfig.java
new file mode 100644
index 0000000..48075fc
--- /dev/null
+++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementGroupConfig.java
@@ -0,0 +1,48 @@
+/*
+ * 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.metrics.publish.spectator;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class MeasurementGroupConfig {
+  // key is measurement id name
+  private Map<String, List<TagFinder>> groups = new HashMap<>();
+
+  public MeasurementGroupConfig() {
+  }
+
+  public MeasurementGroupConfig(String idName, Object... tagNameOrFinders) {
+    addGroup(idName, tagNameOrFinders);
+  }
+
+  public void addGroup(String idName, Object... tagNameOrFinders) {
+    groups.put(idName,
+        Arrays
+            .asList(tagNameOrFinders)
+            .stream()
+            .map(r -> TagFinder.build(r))
+            .collect(Collectors.toList()));
+  }
+
+  public List<TagFinder> findTagFinders(String idName) {
+    return groups.get(idName);
+  }
+}
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
new file mode 100644
index 0000000..2d82113
--- /dev/null
+++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementNode.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.foundation.metrics.publish.spectator;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.netflix.spectator.api.Measurement;
+
+public class MeasurementNode {
+  private String name;
+
+  private List<Measurement> measurements = new ArrayList<>();
+
+  private Map<String, MeasurementNode> children;
+
+  public MeasurementNode(String name, Map<String, MeasurementNode> children) {
+    this.name = name;
+    this.children = children;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public Map<String, MeasurementNode> getChildren() {
+    return children;
+  }
+
+  public MeasurementNode findChild(String childName) {
+    if (children == null) {
+      return null;
+    }
+    return children.get(childName);
+  }
+
+  public MeasurementNode findChild(String... childNames) {
+    MeasurementNode node = this;
+    for (String childName : childNames) {
+      if (node == null) {
+        return null;
+      }
+
+      node = node.findChild(childName);
+    }
+    return node;
+  }
+
+  public MeasurementNode addChild(String childName, Measurement measurement) {
+    if (children == null) {
+      children = new HashMap<>();
+    }
+
+    MeasurementNode node = children.computeIfAbsent(childName, name -> {
+      return new MeasurementNode(name, null);
+    });
+    node.addMeasurement(measurement);
+
+    return node;
+  }
+
+  public List<Measurement> getMeasurements() {
+    return measurements;
+  }
+
+  public void addMeasurement(Measurement measurement) {
+    measurements.add(measurement);
+  }
+}
diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementTree.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementTree.java
new file mode 100644
index 0000000..056bba6
--- /dev/null
+++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementTree.java
@@ -0,0 +1,67 @@
+/*
+ * 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.metrics.publish.spectator;
+
+import java.util.Iterator;
+import java.util.List;
+
+import com.netflix.spectator.api.Id;
+import com.netflix.spectator.api.Measurement;
+import com.netflix.spectator.api.Meter;
+import com.netflix.spectator.api.Tag;
+
+// like select * from meters group by ......
+// but output a tree not a table
+public class MeasurementTree extends MeasurementNode {
+  public MeasurementTree() {
+    super(null, null);
+  }
+
+  // groupConfig:
+  //   key: id name
+  //   value: id tag keys
+  // only id name exists in groupConfig will accept, others will be ignored
+  public void from(Iterator<Meter> meters, MeasurementGroupConfig groupConfig) {
+    meters.forEachRemaining(meter -> {
+      Iterable<Measurement> measurements = meter.measure();
+      from(measurements, groupConfig);
+    });
+  }
+
+  public void from(Iterable<Measurement> measurements, MeasurementGroupConfig groupConfig) {
+    for (Measurement measurement : measurements) {
+      Id id = measurement.id();
+      List<TagFinder> tagFinders = groupConfig.findTagFinders(id.name());
+      if (tagFinders == null) {
+        continue;
+      }
+
+      MeasurementNode node = addChild(id.name(), measurement);
+      for (TagFinder tagFinder : tagFinders) {
+        Tag tag = tagFinder.find(id.tags());
+        if (tag == null) {
+          throw new IllegalStateException(
+              String.format("tag key \"%s\" not exist in %s",
+                  tagFinder.getTagKey(),
+                  measurement));
+        }
+
+        node = node.addChild(tag.value(), measurement);
+      }
+    }
+  }
+}
diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementGroupConfig.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementGroupConfig.java
new file mode 100644
index 0000000..51b6a56
--- /dev/null
+++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementGroupConfig.java
@@ -0,0 +1,72 @@
+/*
+ * 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.metrics.publish.spectator;
+
+import java.util.List;
+import java.util.Map;
+
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+import mockit.Deencapsulation;
+
+public class TestMeasurementGroupConfig {
+  MeasurementGroupConfig config = new MeasurementGroupConfig();
+
+  Map<String, List<TagFinder>> groups = Deencapsulation.getField(config, "groups");
+
+  @Test
+  public void defaultConstruct() {
+    Assert.assertTrue(groups.isEmpty());
+  }
+
+  @Test
+  public void constructAddGroup() {
+    config = new MeasurementGroupConfig("id", "tag1");
+    groups = Deencapsulation.getField(config, "groups");
+
+    Assert.assertThat(groups.keySet(), Matchers.contains("id"));
+    Assert.assertThat(groups.get("id").stream().map(e -> {
+      return e.getTagKey();
+    }).toArray(), Matchers.arrayContaining("tag1"));
+  }
+
+  @Test
+  public void addGroup() {
+    config.addGroup("id1", "tag1.1", "tag1.2");
+    config.addGroup("id2", "tag2.1", "tag2.2");
+
+    Assert.assertThat(groups.keySet(), Matchers.contains("id2", "id1"));
+    Assert.assertThat(groups.get("id1").stream().map(e -> {
+      return e.getTagKey();
+    }).toArray(), Matchers.arrayContaining("tag1.1", "tag1.2"));
+    Assert.assertThat(groups.get("id2").stream().map(e -> {
+      return e.getTagKey();
+    }).toArray(), Matchers.arrayContaining("tag2.1", "tag2.2"));
+  }
+
+  @Test
+  public void findTagReaders() {
+    config.addGroup("id1", "tag1.1", "tag1.2");
+    config.addGroup("id2", "tag2.1", "tag2.2");
+
+    Assert.assertThat(config.findTagFinders("id2").stream().map(e -> {
+      return e.getTagKey();
+    }).toArray(), Matchers.arrayContaining("tag2.1", "tag2.2"));
+  }
+}
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
new file mode 100644
index 0000000..cf55e56
--- /dev/null
+++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementNode.java
@@ -0,0 +1,82 @@
+/*
+ * 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.metrics.publish.spectator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.netflix.spectator.api.Measurement;
+
+import mockit.Mocked;
+
+public class TestMeasurementNode {
+  MeasurementNode node = new MeasurementNode("name", null);
+
+  @Test
+  public void getName() {
+    Assert.assertEquals("name", node.getName());
+  }
+
+  @Test
+  public void getChildren() {
+    Map<String, MeasurementNode> children = new HashMap<>();
+    node = new MeasurementNode("name", children);
+
+    Assert.assertSame(children, node.getChildren());
+  }
+
+  @Test
+  public void findChild_noChildren() {
+    Assert.assertNull(node.findChild("child"));
+  }
+
+  @Test
+  public void findChild_multiLevel_noMiddleChildren(@Mocked Measurement measurement) {
+    MeasurementNode c1 = node.addChild("c1", measurement);
+    c1.addChild("c2", measurement);
+
+    Assert.assertNull(node.findChild("c1_notExist", "c2"));
+  }
+
+  @Test
+  public void findChild_multiLevel_ok(@Mocked Measurement measurement) {
+    MeasurementNode c1 = node.addChild("c1", measurement);
+    MeasurementNode c2 = c1.addChild("c2", measurement);
+
+    Assert.assertSame(c2, node.findChild("c1", "c2"));
+  }
+
+  @Test
+  public void addChild(@Mocked Measurement measurement) {
+    MeasurementNode c1 = node.addChild("c1", measurement);
+    MeasurementNode c2 = node.addChild("c2", measurement);
+
+    Assert.assertSame(c1, node.findChild("c1"));
+    Assert.assertSame(c2, node.findChild("c2"));
+  }
+
+  @Test
+  public void getMeasurements(@Mocked Measurement measurement) {
+    node.addMeasurement(measurement);
+
+    Assert.assertThat(node.getMeasurements(), Matchers.contains(measurement));
+  }
+}
diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementTree.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementTree.java
new file mode 100644
index 0000000..a3eb50d
--- /dev/null
+++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementTree.java
@@ -0,0 +1,84 @@
+/*
+ * 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.metrics.publish.spectator;
+
+import java.util.concurrent.TimeUnit;
+
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import com.netflix.spectator.api.DefaultRegistry;
+import com.netflix.spectator.api.ManualClock;
+import com.netflix.spectator.api.Registry;
+import com.netflix.spectator.api.Statistic;
+import com.netflix.spectator.api.Timer;
+
+public class TestMeasurementTree {
+  MeasurementTree tree = new MeasurementTree();
+
+  ManualClock clock = new ManualClock();
+
+  Registry registry = new DefaultRegistry(clock);
+
+  Timer timer;
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Before
+  public void setup() {
+    timer = registry.timer("id",
+        "g1",
+        "g1v",
+        "g2",
+        "g2v",
+        "t3",
+        "t3v",
+        "t4",
+        "t4v");
+    registry.counter("id_notCare");
+  }
+
+  @Test
+  public void from() {
+    timer.record(10, TimeUnit.NANOSECONDS);
+    timer.record(2, TimeUnit.NANOSECONDS);
+
+    MeasurementGroupConfig config = new MeasurementGroupConfig("id", "g1", "g2", Statistic.count.key());
+    tree.from(registry.iterator(), config);
+
+    Assert.assertEquals(1, tree.getChildren().size());
+
+    MeasurementNode node = tree.findChild("id", "g1v", "g2v");
+    Assert.assertEquals(2d, node.findChild(Statistic.count.value()).getMeasurements().get(0).value(), 0);
+    Assert.assertEquals(12d, node.findChild(Statistic.totalTime.value()).getMeasurements().get(0).value(), 0);
+  }
+
+  @Test
+  public void from_failed() {
+    expectedException.expect(IllegalStateException.class);
+    expectedException.expectMessage(Matchers
+        .is("tag key \"notExist\" not exist in Measurement(id:g1=g1v:g2=g2v:statistic=count:t3=t3v:t4=t4v,0,0.0)"));
+
+    MeasurementGroupConfig config = new MeasurementGroupConfig("id", "notExist");
+    tree.from(registry.iterator(), config);
+  }
+}

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