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 <[email protected]> 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 [email protected].
