You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ha...@apache.org on 2020/09/21 16:12:42 UTC

[skywalking] branch meter-mal created (now 4232448)

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

hanahmily pushed a change to branch meter-mal
in repository https://gitbox.apache.org/repos/asf/skywalking.git.


      at 4232448  Document MAL

This branch includes the following new commits:

     new 4232448  Document MAL

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[skywalking] 01/01: Document MAL

Posted by ha...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hanahmily pushed a commit to branch meter-mal
in repository https://gitbox.apache.org/repos/asf/skywalking.git

commit 42324480fa68d482c58c1e9546facffeffdde0c0
Author: Gao Hongtao <ha...@gmail.com>
AuthorDate: Tue Sep 22 00:11:45 2020 +0800

    Document MAL
    
    Signed-off-by: Gao Hongtao <ha...@gmail.com>
---
 docs/en/concepts-and-designs/mal.md                | 188 +++++++++++++++++++++
 docs/en/concepts-and-designs/meter.md              |   2 +
 oap-server/analyzer/{ => meter-analyzer}/pom.xml   |  29 ++--
 .../skywalking/oap/meter/analyzer/dsl/DSL.java     |  34 ++++
 .../oap/meter/analyzer/dsl/Expression.java         |  62 +++++++
 .../skywalking/oap/meter/analyzer/dsl/Result.java  |  47 ++++++
 .../skywalking/oap/meter/analyzer/dsl/Sample.java  |  42 +++++
 .../oap/meter/analyzer/dsl/SampleFamily.java       | 163 ++++++++++++++++++
 .../skywalking/oap/meter/analyzer/dsl/DSLTest.java | 104 ++++++++++++
 oap-server/analyzer/pom.xml                        |   1 +
 10 files changed, 653 insertions(+), 19 deletions(-)

diff --git a/docs/en/concepts-and-designs/mal.md b/docs/en/concepts-and-designs/mal.md
new file mode 100644
index 0000000..0012d3b
--- /dev/null
+++ b/docs/en/concepts-and-designs/mal.md
@@ -0,0 +1,188 @@
+# Meter Analysis Language
+
+Meter system provides a functional analysis language called MAL(Meter Analysis Language) that lets the user analyze and 
+aggregate meter metric data in OAP streaming system. The result of an expression can either be ingested by agent analyzer,
+or OC/Prometheus analyzer.
+
+## Language data type
+
+In MAL, an expression or sub-expression can evaluate to one of two types:
+
+ - Sample family -  a set of samples(metrics) containing a range of metrics whose name is identical.
+ - Scalar - a simple numeric value. it supports integer/long, floating/double,
+
+## Sample family
+
+A set of samples, which is as the basic unit in MAL. For example:
+
+```
+instance_trace_count
+```
+
+The above sample family might contains following simples which are provided by external modules, for instance, agent analyzer:
+
+```
+instance_trace_count{region="us-west",az="az-1"} 100
+instance_trace_count{region="us-east",az="az-3"} 20
+instance_trace_count{region="asia-north",az="az-1"} 33
+```
+
+### Tag filter
+
+MAL support four type operations to filter samples in a sample family:
+
+ - tagEqual: Filter tags that are exactly equal to the provided string.
+ - tagNotEqual: Filter tags that are not equal to the provided string.
+ - tagMatch: Filter tags that regex-match the provided string.
+ - tagNotMatch: Filter labels that do not regex-match the provided string.
+
+For example, this filters all instance_trace_count samples for us-west and asia-north region and az-1 az:
+
+```
+instance_trace_count.tagMatch("region", "us-west|asia-north").tagEqual("az", "az-1")
+```
+
+### Binary operators
+
+The following binary arithmetic operators are available in MAL:
+
+ - + (addition)
+ - - (subtraction)
+ - * (multiplication)
+ - / (division)
+
+Binary operators are defined between scalar/scalar, sampleFamily/scalar and sampleFamily/sampleFamily value pairs.
+
+Between two scalars: they evaluate to another scalar that is the result of the operator applied to both scalar operands:
+
+```
+1 + 2
+```
+
+Between a sample family and a scalar, the operator is applied to the value of every sample in the smaple family. For example:
+
+```
+instance_trace_count + 2
+``` 
+
+results in
+
+```
+instance_trace_count{region="us-west",az="az-1"} 102 // 100 + 2
+instance_trace_count{region="us-east",az="az-3"} 22 // 20 + 2
+instance_trace_count{region="asia-north",az="az-1"} 35 // 33 + 2
+```
+
+Between two sample families, a binary operator is applied to each sample in the left-hand side sample family and 
+its matching sample in the right-hand sample family. A new sample family with empty name will be generated.
+Only the matched tags will be reserved. Samples for which no matching sample in the right-hand sample family are not in the result.
+
+Another sample family `instance_trace_analysis_error_count` is 
+
+```
+instance_trace_analysis_error_count{region="us-west",az="az-1"} 20
+instance_trace_analysis_error_count{region="asia-north",az="az-1"} 11 
+```
+
+Example expression:
+
+```
+instance_trace_analysis_error_count / instance_trace_count
+```
+
+This returns a result sample family containing the error rate of trace analysis. The samples with region us-west and az az-3 
+have no match and will not show up in the result:
+
+```
+{region="us-west",az="az-1"} 0.8  // 20 / 100
+{region="asia-north",az="az-1"} 0.3333  // 11 / 33
+```
+
+### Aggregation Operation
+
+Sample family supports the following aggregation operations that can be used to aggregate the samples of a single sample family,
+resulting in a new sample family of fewer samples(even single one) with aggregated values:
+
+ - sum (calculate sum over dimensions)
+ - min (select minimum over dimensions)
+ - max (select maximum over dimensions)
+ - avg (calculate the average over dimensions)
+ 
+These operations can be used to aggregate over all label dimensions or preserve distinct dimensions by inputting `by` parameter. 
+
+```
+<aggr-op>(by: <tag1, tag2, ...>)
+```
+
+Example expression:
+
+```
+instance_trace_count.sum(by: ['az'])
+```
+
+will output a result:
+
+```
+instance_trace_count{az="az-1"} 133 // 100 + 33
+instance_trace_count{az="az-3"} 20
+```
+
+### Function
+
+`Duraton` is a textual representation of a time range. The formats accepted are based on the ISO-8601 duration format {@code PnDTnHnMn.nS}
+ with days considered to be exactly 24 hours.
+
+Examples:
+ - "PT20.345S" -- parses as "20.345 seconds"
+ - "PT15M"     -- parses as "15 minutes" (where a minute is 60 seconds)
+ - "PT10H"     -- parses as "10 hours" (where an hour is 3600 seconds)
+ - "P2D"       -- parses as "2 days" (where a day is 24 hours or 86400 seconds)
+ - "P2DT3H4M"  -- parses as "2 days, 3 hours and 4 minutes"
+ - "P-6H3M"    -- parses as "-6 hours and +3 minutes"
+ - "-P6H3M"    -- parses as "-6 hours and -3 minutes"
+ - "-P-6H+3M"  -- parses as "+6 hours and -3 minutes"
+
+#### increase
+`increase(Duration)`. Calculates the increase in the time range.
+
+#### rate
+`rate(Duration)`. Calculates the per-second average rate of increase of the time range.
+
+#### irate
+`irate()`. Calculates the per-second instant rate of increase of the time range.
+
+#### tag
+`tag({allTags -> })`. Update tags of samples. User can add, drop, rename and update tags.
+
+#### histogram
+`histogram(le: '<the tag name of le>')`. Transforms less based histogram buckets to meter system histogram buckets. 
+`le` parameter hints the tag name of a bucket. 
+
+#### histogram_quantile
+`histogram_quantile([<φ scalar>])`. Hints meter-system to calculates the φ-quantile (0 ≤ φ ≤ 100) from the buckets. 
+
+## Down Sampling Operation
+MAL should instruct meter-system how to do downsampling for metrics. It doesn't only refer to aggregate raw samples to 
+`minute` level, but also hints data from `minute` to higher levels, for instance, `hour` and `day`. 
+
+Down sampling operations are as global function in MAL:
+
+ - avg
+ - latest
+ - min
+ - max
+ - mean
+ - sum
+ - count
+
+The default one is `avg` if not specific an operation.
+
+If user want get latest time from `last_server_state_sync_time_in_seconds`:
+
+```
+latest(last_server_state_sync_time_in_seconds.tagEqual('production', 'catalog'))
+
+or
+
+latest last_server_state_sync_time_in_seconds.tagEqual('production', 'catalog')
+```
diff --git a/docs/en/concepts-and-designs/meter.md b/docs/en/concepts-and-designs/meter.md
index d07160d..270d347 100644
--- a/docs/en/concepts-and-designs/meter.md
+++ b/docs/en/concepts-and-designs/meter.md
@@ -22,3 +22,5 @@ Meter System supports following binding functions
 - **percentile**. Read [percentile in WIKI](https://en.wikipedia.org/wiki/Percentile). Unlike in the OAL, we provide
 50/75/90/95/99 in default, in the meter system function, percentile function accepts several ranks, which should be in
 the (0, 100) range.
+
+## 
diff --git a/oap-server/analyzer/pom.xml b/oap-server/analyzer/meter-analyzer/pom.xml
similarity index 56%
copy from oap-server/analyzer/pom.xml
copy to oap-server/analyzer/meter-analyzer/pom.xml
index 90c758d..0957a11 100644
--- a/oap-server/analyzer/pom.xml
+++ b/oap-server/analyzer/meter-analyzer/pom.xml
@@ -17,36 +17,27 @@
   ~
   -->
 
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <artifactId>oap-server</artifactId>
+        <artifactId>analyzer</artifactId>
         <groupId>org.apache.skywalking</groupId>
         <version>8.2.0-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <artifactId>analyzer</artifactId>
-    <packaging>pom</packaging>
-
-    <modules>
-        <module>agent-analyzer</module>
-    </modules>
+    <artifactId>meter-analyzer</artifactId>
 
     <dependencies>
         <dependency>
-            <groupId>org.apache.skywalking</groupId>
-            <artifactId>apm-network</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.skywalking</groupId>
-            <artifactId>library-module</artifactId>
-            <version>${project.version}</version>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.apache.skywalking</groupId>
-            <artifactId>library-util</artifactId>
-            <version>${project.version}</version>
+            <groupId>io.vavr</groupId>
+            <artifactId>vavr</artifactId>
         </dependency>
     </dependencies>
+
 </project>
\ No newline at end of file
diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/DSL.java b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/DSL.java
new file mode 100644
index 0000000..24a287f
--- /dev/null
+++ b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/DSL.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.skywalking.oap.meter.analyzer.dsl;
+
+import groovy.lang.Binding;
+import groovy.lang.GroovyShell;
+import groovy.util.DelegatingScript;
+import org.codehaus.groovy.control.CompilerConfiguration;
+
+public final class DSL {
+    public static Expression parse(final String expression) {
+        CompilerConfiguration cc = new CompilerConfiguration();
+        cc.setScriptBaseClass(DelegatingScript.class.getName());
+        GroovyShell sh = new GroovyShell(new Binding(), cc);
+        DelegatingScript script = (DelegatingScript) sh.parse(expression);
+        return new Expression(script);
+    }
+}
diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Expression.java b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Expression.java
new file mode 100644
index 0000000..c58f19b
--- /dev/null
+++ b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Expression.java
@@ -0,0 +1,62 @@
+/*
+ * 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.skywalking.oap.meter.analyzer.dsl;
+
+import com.google.common.collect.ImmutableMap;
+import groovy.lang.GroovyObjectSupport;
+import groovy.util.DelegatingScript;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class Expression {
+
+    private final DelegatingScript expression;
+
+    Expression(DelegatingScript expression) {
+        this.expression = expression;
+    }
+
+    public Result run(final ImmutableMap<String, SampleFamily> sampleFamilies) {
+        expression.setDelegate(new GroovyObjectSupport() {
+
+            public SampleFamily propertyMissing(String metricName) {
+                if (sampleFamilies.containsKey(metricName)) {
+                    return sampleFamilies.get(metricName);
+                }
+                if (log.isDebugEnabled()) {
+                    log.debug("{} doesn't exist in {}", metricName, sampleFamilies.keySet());
+                }
+                throw new IllegalArgumentException("[" + metricName + "] can't be found");
+            }
+
+            @Override public Object invokeMethod(String name, Object args) {
+                //TODO: Validate the name is one of meter functions
+                return super.invokeMethod(name, args);
+            }
+
+        });
+        try {
+            SampleFamily sf = (SampleFamily) expression.run();
+            return Result.success(sf);
+        } catch (Throwable t) {
+            return Result.fail(t);
+        }
+    }
+
+}
diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Result.java b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Result.java
new file mode 100644
index 0000000..4c1b2d0
--- /dev/null
+++ b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Result.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.skywalking.oap.meter.analyzer.dsl;
+
+import lombok.AccessLevel;
+import lombok.EqualsAndHashCode;
+import lombok.RequiredArgsConstructor;
+import lombok.ToString;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+@EqualsAndHashCode
+@ToString
+public class Result {
+    public static Result fail(final Throwable throwable) {
+        log.error("Result is failure", throwable);
+        return new Result(false, SampleFamily.EMPTY);
+    }
+
+    public static Result success(SampleFamily sf) {
+        if (log.isDebugEnabled()) {
+            log.debug("Result is successful, sample family is {}", sf);
+        }
+        return new Result(true, sf);
+    }
+
+    private final boolean success;
+
+    private final SampleFamily data;
+}
diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Sample.java b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Sample.java
new file mode 100644
index 0000000..6daf65a
--- /dev/null
+++ b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Sample.java
@@ -0,0 +1,42 @@
+/*
+ * 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.skywalking.oap.meter.analyzer.dsl;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.function.Function;
+import lombok.Builder;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Builder
+@EqualsAndHashCode
+@ToString
+public class Sample {
+    final ImmutableMap<String, String> labels;
+    final double value;
+    final long timeStamp;
+
+    Sample newValue(Function<Double, Double> transform) {
+        Sample.SampleBuilder b =  Sample.builder();
+        b.timeStamp(timeStamp);
+        b.labels(labels);
+        b.value(transform.apply(value));
+        return b.build();
+    }
+}
diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/SampleFamily.java b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/SampleFamily.java
new file mode 100644
index 0000000..5c31905
--- /dev/null
+++ b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/SampleFamily.java
@@ -0,0 +1,163 @@
+/*
+ * 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.skywalking.oap.meter.analyzer.dsl;
+
+import com.google.common.base.Preconditions;
+import groovy.lang.Closure;
+import io.vavr.Function2;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import lombok.AccessLevel;
+import lombok.EqualsAndHashCode;
+import lombok.RequiredArgsConstructor;
+import lombok.ToString;
+
+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+@EqualsAndHashCode
+@ToString
+public class SampleFamily {
+    public static final SampleFamily EMPTY = new SampleFamily(new Sample[0]);
+
+    public static SampleFamily build(Sample... samples) {
+        return new SampleFamily(samples);
+    }
+
+    private final Sample[] samples;
+
+    /**
+     * Following operations are used in DSL
+     */
+
+    /* tag filter operations*/
+    public SampleFamily tagEqual(String... labels) {
+        return match(labels, String::equals);
+    }
+
+    public SampleFamily tagNotEqual(String[] labels) {
+        return match(labels, (sv, lv) -> !sv.equals(lv));
+    }
+
+    public SampleFamily tagMatch(String[] labels) {
+        return match(labels, (sv, lv) -> lv.matches(sv));
+    }
+
+    public SampleFamily tagNotMatch(String[] labels) {
+        return match(labels, (sv, lv) -> !lv.matches(sv));
+    }
+
+    /* Operator overloading*/
+    public SampleFamily plus(Number number) {
+        return newValue(v -> v + number.doubleValue());
+    }
+
+    public SampleFamily minus(Number number) {
+        return newValue(v -> v - number.doubleValue());
+    }
+
+    public SampleFamily multiply(Number number) {
+        return newValue(v -> v * number.doubleValue());
+    }
+
+    public SampleFamily div(Number number) {
+        return newValue(v -> v / number.doubleValue());
+    }
+
+    public SampleFamily plus(SampleFamily number) {
+        return this;
+    }
+
+    public SampleFamily minus(SampleFamily number) {
+        return this;
+    }
+
+    public SampleFamily multiply(SampleFamily number) {
+        return this;
+    }
+
+    public SampleFamily div(SampleFamily number) {
+        return this;
+    }
+
+    /* Aggregation operators */
+    @RequiredArgsConstructor
+    public static class AggregationParameter {
+        private final String[] by;
+    }
+
+    public SampleFamily sum(AggregationParameter parameter) {
+        return this;
+    }
+
+    /* Function */
+    public SampleFamily increase(String range) {
+        return this;
+    }
+
+    public SampleFamily rate(String range) {
+        return this;
+    }
+
+    public SampleFamily irate() {
+        return this;
+    }
+
+    public SampleFamily tag(Closure cl) {
+        return this;
+    }
+
+    @RequiredArgsConstructor
+    public static class HistogramParameter {
+        private final String le;
+    }
+
+    public  SampleFamily histogram(HistogramParameter parameter) {
+       return this;
+    }
+
+    public SampleFamily histogram_quantile(int[] range) {
+        return  this;
+    }
+
+    private SampleFamily match(String[] labels, Function2<String, String, Boolean> op) {
+        Preconditions.checkArgument(labels.length % 2 == 0);
+        Map<String, String> ll = new HashMap<>(labels.length / 2);
+        for (int i = 0; i < labels.length; i += 2) {
+            ll.put(labels[i], labels[i + 1]);
+        }
+        Stream<Sample> ss = Arrays.stream(samples).filter(sample ->
+            ll.entrySet().stream().allMatch(entry ->
+                sample.labels.containsKey(entry.getKey()) && op.apply(sample.labels.get(entry.getKey()), entry.getValue())));
+        Sample[] sArr = ss.toArray(Sample[]::new);
+        if (sArr.length < 1) {
+            return SampleFamily.EMPTY;
+        }
+        return SampleFamily.build(sArr);
+    }
+
+    private SampleFamily newValue(Function<Double, Double> transform) {
+        Sample[] ss = new Sample[samples.length];
+        for (int i = 0; i < ss.length; i++) {
+            ss[i] = samples[i].newValue(transform);
+        }
+        return SampleFamily.build(ss);
+    }
+}
diff --git a/oap-server/analyzer/meter-analyzer/src/test/java/org/apache/skywalking/oap/meter/analyzer/dsl/DSLTest.java b/oap-server/analyzer/meter-analyzer/src/test/java/org/apache/skywalking/oap/meter/analyzer/dsl/DSLTest.java
new file mode 100644
index 0000000..5324ec8
--- /dev/null
+++ b/oap-server/analyzer/meter-analyzer/src/test/java/org/apache/skywalking/oap/meter/analyzer/dsl/DSLTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.skywalking.oap.meter.analyzer.dsl;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static com.google.common.collect.ImmutableMap.of;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.CoreMatchers.is;
+
+@RunWith(Parameterized.class)
+public class DSLTest {
+
+    @Parameterized.Parameter
+    public String name;
+
+    @Parameterized.Parameter(1)
+    public ImmutableMap<String, SampleFamily> input;
+
+    @Parameterized.Parameter(2)
+    public String expression;
+
+    @Parameterized.Parameter(3)
+    public Result want;
+
+    @Parameterized.Parameters(name = "{index}: {0}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+            {
+               "default",
+                of("instance_cpu_percentage", SampleFamily.EMPTY),
+                "instance_cpu_percentage",
+                Result.success(SampleFamily.EMPTY),
+            },
+            {
+                "single-value",
+                of("instance_cpu_percentage", SampleFamily.build(Sample.builder().value(1600592418480.0).build())),
+                "instance_cpu_percentage",
+                Result.success(SampleFamily.build(Sample.builder().value(1600592418480.0).build())),
+            },
+            {
+                "label-equal",
+                of("instance_cpu_percentage", SampleFamily.build(
+                    Sample.builder().labels(of("idc", "t1")).value(1600592418480.0).build(),
+                    Sample.builder().labels(of("idc", "t2")).value(1600592418481.0).build()
+                )),
+                "instance_cpu_percentage.tagEqual('idc','t1')",
+                Result.success(SampleFamily.build(
+                    Sample.builder().labels(of("idc", "t1")).value(1600592418480.0).build()
+                )),
+            },
+            {
+                "label-not-equal",
+                of("instance_cpu_percentage", SampleFamily.build(
+                    Sample.builder().labels(of("idc", "t1")).value(1600592418480.0).build(),
+                    Sample.builder().labels(of("idc", "t2")).value(1600592418481.0).build()
+                )),
+                "instance_cpu_percentage.tagNotEqual('idc','t2')",
+                Result.success(SampleFamily.build(
+                    Sample.builder().labels(of("idc", "t1")).value(1600592418480.0).build()
+                )),
+            },
+            {
+                "plus",
+                of("instance_cpu_percentage", SampleFamily.build(
+                    Sample.builder().labels(of("idc", "t1")).value(1600592418480.0).build(),
+                    Sample.builder().labels(of("idc", "t2")).value(1600592418481.0).build()
+                )),
+                "instance_cpu_percentage.tagEqual('idc','t1') + 1000",
+                Result.success(SampleFamily.build(
+                    Sample.builder().labels(of("idc", "t1")).value(1600592419480.0).build()
+                )),
+            },
+        });
+    }
+
+    @Test
+    public void test() {
+        Expression e = DSL.parse(expression);
+        Result r = e.run(input);
+        assertThat(r, is(want));
+    }
+}
\ No newline at end of file
diff --git a/oap-server/analyzer/pom.xml b/oap-server/analyzer/pom.xml
index 90c758d..b147071 100644
--- a/oap-server/analyzer/pom.xml
+++ b/oap-server/analyzer/pom.xml
@@ -30,6 +30,7 @@
 
     <modules>
         <module>agent-analyzer</module>
+        <module>meter-analyzer</module>
     </modules>
 
     <dependencies>