You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2021/03/20 09:13:00 UTC

[skywalking] branch master updated: Add functions in MAL to filter metrics according to the metric value (#6587)

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

wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking.git


The following commit(s) were added to refs/heads/master by this push:
     new c2c3830  Add functions in MAL to filter metrics according to the metric value (#6587)
c2c3830 is described below

commit c2c3830a8059262028a120783896d149284c1174
Author: wankai123 <wa...@foxmail.com>
AuthorDate: Sat Mar 20 17:12:34 2021 +0800

    Add functions in MAL to filter metrics according to the metric value (#6587)
---
 CHANGES.md                                         |   1 +
 docs/en/concepts-and-designs/mal.md                |  16 ++
 .../oap/meter/analyzer/dsl/SampleFamily.java       |  58 ++++++++
 .../oap/meter/analyzer/dsl/ValueFilterTest.java    | 161 +++++++++++++++++++++
 4 files changed, 236 insertions(+)

diff --git a/CHANGES.md b/CHANGES.md
index 8647313..8e1d7e3 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -53,6 +53,7 @@ Release Notes.
 * Fix receiver don't need to get itself when healthCheck
 * Remove group concept from AvgHistogramFunction. Heatmap(function result) doesn't support labels.
 * Support metrics grouped by scope labelValue in MAL, no need global same labelValue as before.
+* Add functions in MAL to filter metrics according to the metric value.
 * Optimize the self monitoring grafana dashboard.
 
 #### UI
diff --git a/docs/en/concepts-and-designs/mal.md b/docs/en/concepts-and-designs/mal.md
index d61b583..7770b62 100644
--- a/docs/en/concepts-and-designs/mal.md
+++ b/docs/en/concepts-and-designs/mal.md
@@ -41,6 +41,22 @@ For example, this filters all instance_trace_count samples for us-west and asia-
 ```
 instance_trace_count.tagMatch("region", "us-west|asia-north").tagEqual("az", "az-1")
 ```
+### Value filter
+
+MAL support six type operations to filter samples in a sample family by value:
+
+- valueEqual: Filter values that are exactly equal to the provided value.
+- valueNotEqual: Filter values that are not equal to the provided value.
+- valueGreater: Filter values that greater than the provided value.
+- valueGreaterEqual: Filter values that greater or equal the provided value.
+- valueLess: Filter values that less than the provided value.
+- valueLessEqual: Filter values that less or equal the provided value.
+
+For example, this filters all instance_trace_count samples for values >= 33:
+
+```
+instance_trace_count.valueGreaterEqual(33)
+```
 
 ### Binary operators
 
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
index d2ef900..be53356 100644
--- 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
@@ -26,6 +26,7 @@ import com.google.common.collect.Maps;
 import com.google.common.util.concurrent.AtomicDouble;
 import groovy.lang.Closure;
 import io.vavr.Function2;
+import io.vavr.Function3;
 import lombok.AccessLevel;
 import lombok.Builder;
 import lombok.EqualsAndHashCode;
@@ -101,6 +102,31 @@ public class SampleFamily {
         return match(labels, (sv, lv) -> !sv.matches(lv));
     }
 
+    /* value filter operations*/
+    public SampleFamily valueEqual(double compValue) {
+        return valueMatch(CompType.EQUAL, compValue, InternalOps::doubleComp);
+    }
+
+    public SampleFamily valueNotEqual(double compValue) {
+        return valueMatch(CompType.NOT_EQUAL, compValue, InternalOps::doubleComp);
+    }
+
+    public SampleFamily valueGreater(double compValue) {
+        return valueMatch(CompType.GREATER, compValue, InternalOps::doubleComp);
+    }
+
+    public SampleFamily valueGreaterEqual(double compValue) {
+        return valueMatch(CompType.GREATER_EQUAL, compValue, InternalOps::doubleComp);
+    }
+
+    public SampleFamily valueLess(double compValue) {
+        return valueMatch(CompType.LESS, compValue, InternalOps::doubleComp);
+    }
+
+    public SampleFamily valueLessEqual(double compValue) {
+        return valueMatch(CompType.LESS_EQUAL, compValue, InternalOps::doubleComp);
+    }
+
     /* Binary operator overloading*/
     public SampleFamily plus(Number number) {
         return newValue(v -> v + number.doubleValue());
@@ -401,6 +427,14 @@ public class SampleFamily {
         return ss.length > 0 ? SampleFamily.build(this.context, ss) : EMPTY;
     }
 
+    private SampleFamily valueMatch(CompType compType,
+                                    double compValue,
+                                    Function3<CompType, Double, Double, Boolean> op) {
+        Sample[] ss = Arrays.stream(samples)
+                            .filter(sample -> op.apply(compType, sample.value, compValue)).toArray(Sample[]::new);
+        return ss.length > 0 ? SampleFamily.build(this.context, ss) : EMPTY;
+    }
+
     SampleFamily newValue(Function<Double, Double> transform) {
         if (this == EMPTY) {
             return EMPTY;
@@ -509,6 +543,26 @@ public class SampleFamily {
             return a.equals(b);
         }
 
+        private static boolean doubleComp(CompType compType, double a, double b) {
+            int result = Double.compare(a, b);
+            switch (compType) {
+                case EQUAL:
+                    return result == 0;
+                case NOT_EQUAL:
+                    return result != 0;
+                case GREATER:
+                    return result == 1;
+                case GREATER_EQUAL:
+                    return result == 0 || result == 1;
+                case LESS:
+                    return result == -1;
+                case LESS_EQUAL:
+                    return result == 0 || result == -1;
+            }
+
+            return false;
+        }
+
         private static ImmutableMap<String, String> getLabels(final List<String> labelKeys, final Sample sample) {
             return labelKeys.stream()
                             .collect(toImmutableMap(
@@ -517,4 +571,8 @@ public class SampleFamily {
                             ));
         }
     }
+
+    private enum CompType {
+        EQUAL, NOT_EQUAL, LESS, LESS_EQUAL, GREATER, GREATER_EQUAL
+    }
 }
diff --git a/oap-server/analyzer/meter-analyzer/src/test/java/org/apache/skywalking/oap/meter/analyzer/dsl/ValueFilterTest.java b/oap-server/analyzer/meter-analyzer/src/test/java/org/apache/skywalking/oap/meter/analyzer/dsl/ValueFilterTest.java
new file mode 100644
index 0000000..009cf47
--- /dev/null
+++ b/oap-server/analyzer/meter-analyzer/src/test/java/org/apache/skywalking/oap/meter/analyzer/dsl/ValueFilterTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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 lombok.extern.slf4j.Slf4j;
+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.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+@Slf4j
+@RunWith(Parameterized.class)
+public class ValueFilterTest {
+
+    @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.Parameter(4)
+    public boolean isThrow;
+
+    @Parameterized.Parameters(name = "{index}: {0}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] {
+            {
+                "valueEqual",
+                of("http_success_request", SampleFamilyBuilder.newBuilder(
+                    Sample.builder().labels(of("idc", "t1")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t2")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t3")).value(1).build()
+                ).build()),
+                "http_success_request.valueEqual(1)",
+                Result.success(SampleFamilyBuilder.newBuilder(
+                    Sample.builder().labels(of("idc", "t3")).value(1).build()
+                ).build()),
+                false,
+                },
+            {
+                "valueNotEqual",
+                of("http_success_request", SampleFamilyBuilder.newBuilder(
+                    Sample.builder().labels(of("idc", "t1")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t2")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t3")).value(1).build()
+                ).build()),
+                "http_success_request.valueNotEqual(1)",
+                Result.success(SampleFamilyBuilder.newBuilder(
+                    Sample.builder().labels(of("idc", "t1")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t2")).value(2).build()
+                ).build()),
+                false,
+                },
+            {
+                "valueGreater",
+                of("http_success_request", SampleFamilyBuilder.newBuilder(
+                    Sample.builder().labels(of("idc", "t1")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t2")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t3")).value(1).build()
+                ).build()),
+                "http_success_request.valueGreater(1)",
+                Result.success(SampleFamilyBuilder.newBuilder(
+                    Sample.builder().labels(of("idc", "t1")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t2")).value(2).build()
+                ).build()),
+                false,
+                },
+            {
+                "valueGreaterEqual",
+                of("http_success_request", SampleFamilyBuilder.newBuilder(
+                    Sample.builder().labels(of("idc", "t1")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t2")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t3")).value(1).build()
+                ).build()),
+                "http_success_request.valueGreaterEqual(1)",
+                Result.success(SampleFamilyBuilder.newBuilder(
+                    Sample.builder().labels(of("idc", "t1")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t2")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t3")).value(1).build()
+                ).build()),
+                false,
+                },
+            {
+                "valueLess",
+                of("http_success_request", SampleFamilyBuilder.newBuilder(
+                    Sample.builder().labels(of("idc", "t1")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t2")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t3")).value(1).build()
+                ).build()),
+                "http_success_request.valueLess(2)",
+                Result.success(SampleFamilyBuilder.newBuilder(
+                    Sample.builder().labels(of("idc", "t3")).value(1).build()
+                ).build()),
+                false,
+                },
+            {
+                "valueLessEqual",
+                of("http_success_request", SampleFamilyBuilder.newBuilder(
+                    Sample.builder().labels(of("idc", "t1")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t2")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t3")).value(1).build()
+                ).build()),
+                "http_success_request.valueLessEqual(2)",
+                Result.success(SampleFamilyBuilder.newBuilder(
+                    Sample.builder().labels(of("idc", "t1")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t2")).value(2).build(),
+                    Sample.builder().labels(of("idc", "t3")).value(1).build()
+                ).build()),
+                false,
+                },
+            });
+    }
+
+    @Test
+    public void test() {
+        Expression e = DSL.parse(expression);
+        Result r = null;
+        try {
+            r = e.run(input);
+        } catch (Throwable t) {
+            if (isThrow) {
+                return;
+            }
+            log.error("Test failed", t);
+            fail("Should not throw anything");
+        }
+        if (isThrow) {
+            fail("Should throw something");
+        }
+        assertThat(r, is(want));
+    }
+}
\ No newline at end of file