From ecc18b9be3328a0d85356d3bb2329eff4e09b13f Mon Sep 17 00:00:00 2001 From: kezhenxu94 Date: Tue, 11 Aug 2020 09:37:21 +0800 Subject: [PATCH] Support !=, like filter expressions in OAL (#5269) --- .gitignore | 7 +- docs/en/concepts-and-designs/oal.md | 6 +- .../skywalking/oal/rt/grammar/OALLexer.g4 | 4 +- .../skywalking/oal/rt/grammar/OALParser.g4 | 16 +++- .../apache/skywalking/oal/rt/OALRuntime.java | 7 -- .../oal/rt/parser/ConditionExpression.java | 4 + .../oal/rt/parser/DeepAnalysis.java | 90 +++++------------- .../oal/rt/parser/FilterMatchers.java | 95 +++++++++++++++++++ .../oal/rt/parser/MetricsHolder.java | 22 +++-- .../code-templates/dispatcher/doMetrics.ftl | 4 +- .../oal/rt/parser/DeepAnalysisTest.java | 56 ++++++++++- .../oal/rt/parser/ScriptParserTest.java | 2 - .../annotation/BooleanValueFilterMatcher.java | 37 ++++++++ .../metrics/annotation/FilterMatcher.java | 40 ++++++++ .../metrics/expression/BooleanMatch.java | 32 +++++++ .../expression/BooleanNotEqualMatch.java | 32 +++++++ .../metrics/expression/EqualMatch.java | 42 +------- .../metrics/expression/GreaterEqualMatch.java | 3 + .../metrics/expression/GreaterMatch.java | 3 + .../metrics/expression/LessEqualMatch.java | 3 + .../metrics/expression/LessMatch.java | 3 + .../metrics/expression/LikeMatch.java | 36 +++++++ .../metrics/expression/NotEqualMatch.java | 29 ++++++ .../metrics/expression/LikeMatchTest.java | 36 +++++++ 24 files changed, 472 insertions(+), 137 deletions(-) create mode 100644 oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/FilterMatchers.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/annotation/BooleanValueFilterMatcher.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/annotation/FilterMatcher.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/BooleanMatch.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/BooleanNotEqualMatch.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LikeMatch.java create mode 100644 oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/NotEqualMatch.java create mode 100644 oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LikeMatchTest.java diff --git a/.gitignore b/.gitignore index cd2d1bd2c..181769e4c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,11 +14,12 @@ packages/ /docker/snapshot/*.gz .mvn/wrapper/*.jar OALLexer.tokens -.factorypath -.vscode +.factorypath +.vscode .checkstyle .externalToolBuilders /test/plugin/dist /test/plugin/workspace /test/jacoco/classes -/test/jacoco/*.exec \ No newline at end of file +/test/jacoco/*.exec +oap-server/oal-grammar/**/gen/ diff --git a/docs/en/concepts-and-designs/oal.md b/docs/en/concepts-and-designs/oal.md index 7e4b74a9f..62aef850c 100644 --- a/docs/en/concepts-and-designs/oal.md +++ b/docs/en/concepts-and-designs/oal.md @@ -34,7 +34,7 @@ Read [Scope Definitions](scope-definitions.md), you can find all existing Scopes Use filter to build the conditions for the value of fields, by using field name and expression. The expressions support to link by `and`, `or` and `(...)`. -The OPs support `=`, `!=`, `>`, `<`, `in (v1, v2, ...`, `like "%..."`, with type detection based of field type. Trigger compile +The OPs support `==`, `!=`, `>`, `<`, `>=`, `<=`, `like %...`, `like ...%` and `like %...%`, with type detection based of field type. Trigger compile or code generation error if incompatible. ## Aggregation Function @@ -103,7 +103,7 @@ In default, no one is being disable. Endpoint_p99 = from(Endpoint.latency).filter(name in ("Endpoint1", "Endpoint2")).summary(0.99) // Caculate p99 of Endpoint name started with `serv` -serv_Endpoint_p99 = from(Endpoint.latency).filter(name like ("serv%")).summary(0.99) +serv_Endpoint_p99 = from(Endpoint.latency).filter(name like "serv%").summary(0.99) // Caculate the avg response time of each Endpoint Endpoint_avg = from(Endpoint.latency).avg() @@ -112,7 +112,7 @@ Endpoint_avg = from(Endpoint.latency).avg() Endpoint_percentile = from(Endpoint.latency).percentile(10) // Caculate the percent of response status is true, for each service. -Endpoint_success = from(Endpoint.*).filter(status = "true").percent() +Endpoint_success = from(Endpoint.*).filter(status == true).percent() // Caculate the percent of response code in [200, 299], for each service. Endpoint_200 = from(Endpoint.*).filter(responseCode like "2%").percent() diff --git a/oap-server/oal-grammar/src/main/antlr4/org/apache/skywalking/oal/rt/grammar/OALLexer.g4 b/oap-server/oal-grammar/src/main/antlr4/org/apache/skywalking/oal/rt/grammar/OALLexer.g4 index 8ed3398bd..ddf90b50d 100644 --- a/oap-server/oal-grammar/src/main/antlr4/org/apache/skywalking/oal/rt/grammar/OALLexer.g4 +++ b/oap-server/oal-grammar/src/main/antlr4/org/apache/skywalking/oal/rt/grammar/OALLexer.g4 @@ -133,4 +133,6 @@ ALL: '*'; GREATER: '>'; LESS: '<'; GREATER_EQUAL: '>='; -LESS_EQUAL: '<='; \ No newline at end of file +LESS_EQUAL: '<='; +NOT_EQUAL: '!='; +LIKE: 'like'; diff --git a/oap-server/oal-grammar/src/main/antlr4/org/apache/skywalking/oal/rt/grammar/OALParser.g4 b/oap-server/oal-grammar/src/main/antlr4/org/apache/skywalking/oal/rt/grammar/OALParser.g4 index 46818ee7f..12cc62826 100644 --- a/oap-server/oal-grammar/src/main/antlr4/org/apache/skywalking/oal/rt/grammar/OALParser.g4 +++ b/oap-server/oal-grammar/src/main/antlr4/org/apache/skywalking/oal/rt/grammar/OALParser.g4 @@ -88,7 +88,7 @@ literalExpression ; expression - : booleanMatch | stringMatch | greaterMatch | lessMatch | greaterEqualMatch | lessEqualMatch + : booleanMatch | stringMatch | greaterMatch | lessMatch | greaterEqualMatch | lessEqualMatch | notEqualMatch | booleanNotEqualMatch | likeMatch ; booleanMatch @@ -115,6 +115,18 @@ lessEqualMatch : conditionAttribute LESS_EQUAL numberConditionValue ; +booleanNotEqualMatch + : conditionAttribute NOT_EQUAL booleanConditionValue + ; + +notEqualMatch + : conditionAttribute NOT_EQUAL (numberConditionValue | stringConditionValue | enumConditionValue) + ; + +likeMatch + : conditionAttribute LIKE stringConditionValue + ; + conditionAttribute : IDENTIFIER ; @@ -133,4 +145,4 @@ enumConditionValue numberConditionValue : NUMBER_LITERAL - ; \ No newline at end of file + ; diff --git a/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/OALRuntime.java b/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/OALRuntime.java index 6175f81b9..c4f6c340d 100644 --- a/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/OALRuntime.java +++ b/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/OALRuntime.java @@ -53,7 +53,6 @@ import org.apache.skywalking.apm.util.StringUtil; import org.apache.skywalking.oal.rt.output.AllDispatcherContext; import org.apache.skywalking.oal.rt.output.DispatcherContext; import org.apache.skywalking.oal.rt.parser.AnalysisResult; -import org.apache.skywalking.oal.rt.parser.MetricsHolder; import org.apache.skywalking.oal.rt.parser.OALScripts; import org.apache.skywalking.oal.rt.parser.ScriptParser; import org.apache.skywalking.oal.rt.parser.SourceColumn; @@ -144,12 +143,6 @@ public class OALRuntime implements OALEngine { this.currentClassLoader = currentClassLoader; Reader read; - try { - MetricsHolder.init(); - } catch (IOException e) { - throw new ModuleStartException("load metrics functions error.", e); - } - try { read = ResourceUtils.read(oalDefine.getConfigFile()); } catch (FileNotFoundException e) { diff --git a/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/ConditionExpression.java b/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/ConditionExpression.java index c562a70b4..60e90dcda 100644 --- a/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/ConditionExpression.java +++ b/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/ConditionExpression.java @@ -18,11 +18,15 @@ package org.apache.skywalking.oal.rt.parser; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; @Getter @Setter +@NoArgsConstructor +@AllArgsConstructor public class ConditionExpression { // original from script private String expressionType; diff --git a/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/DeepAnalysis.java b/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/DeepAnalysis.java index 344b7aacd..d1d065aef 100644 --- a/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/DeepAnalysis.java +++ b/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/DeepAnalysis.java @@ -24,6 +24,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.List; import org.apache.skywalking.oal.rt.util.ClassMethodUtil; +import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics; import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.Arg; import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.ConstOne; import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.Entrance; @@ -35,8 +36,7 @@ public class DeepAnalysis { // 1. Set sub package name by source.metrics result.setPackageName(result.getSourceName().toLowerCase()); - Class metricsClass = MetricsHolder.find(result - .getAggregationFunctionName()); + Class metricsClass = MetricsHolder.find(result.getAggregationFunctionName()); String metricsClassSimpleName = metricsClass.getSimpleName(); result.setMetricsClassName(metricsClassSimpleName); @@ -45,45 +45,22 @@ public class DeepAnalysis { List expressions = result.getFilterExpressionsParserResult(); if (expressions != null && expressions.size() > 0) { for (ConditionExpression expression : expressions) { - Expression filterExpression = new Expression(); - if ("booleanMatch".equals(expression.getExpressionType())) { - filterExpression.setExpressionObject("EqualMatch"); - filterExpression.setLeft("source." + ClassMethodUtil.toIsMethod(expression.getAttribute()) + "()"); - filterExpression.setRight(expression.getValue()); - result.addFilterExpressions(filterExpression); - } else if ("stringMatch".equals(expression.getExpressionType())) { - filterExpression.setExpressionObject("EqualMatch"); - filterExpression.setLeft("source." + ClassMethodUtil.toGetMethod(expression.getAttribute()) + "()"); - filterExpression.setRight(expression.getValue()); - result.addFilterExpressions(filterExpression); - } else if ("greaterMatch".equals(expression.getExpressionType())) { - filterExpression.setExpressionObject("GreaterMatch"); - filterExpression.setLeft("source." + ClassMethodUtil.toGetMethod(expression.getAttribute()) + "()"); - filterExpression.setRight(expression.getValue()); - result.addFilterExpressions(filterExpression); - } else if ("lessMatch".equals(expression.getExpressionType())) { - filterExpression.setExpressionObject("LessMatch"); - filterExpression.setLeft("source." + ClassMethodUtil.toGetMethod(expression.getAttribute()) + "()"); - filterExpression.setRight(expression.getValue()); - result.addFilterExpressions(filterExpression); - } else if ("greaterEqualMatch".equals(expression.getExpressionType())) { - filterExpression.setExpressionObject("GreaterEqualMatch"); - filterExpression.setLeft("source." + ClassMethodUtil.toGetMethod(expression.getAttribute()) + "()"); - filterExpression.setRight(expression.getValue()); - result.addFilterExpressions(filterExpression); - } else if ("lessEqualMatch".equals(expression.getExpressionType())) { - filterExpression.setExpressionObject("LessEqualMatch"); - filterExpression.setLeft("source." + ClassMethodUtil.toGetMethod(expression.getAttribute()) + "()"); - filterExpression.setRight(expression.getValue()); - result.addFilterExpressions(filterExpression); - } else { - throw new IllegalArgumentException("filter expression [" + expression.getExpressionType() + "] not found"); - } + final FilterMatchers.MatcherInfo matcherInfo = FilterMatchers.INSTANCE.find(expression.getExpressionType()); + + final String getter = matcherInfo.isBooleanType() + ? ClassMethodUtil.toIsMethod(expression.getAttribute()) + : ClassMethodUtil.toGetMethod(expression.getAttribute()); + + final Expression filterExpression = new Expression(); + filterExpression.setExpressionObject(matcherInfo.getMatcher().getName()); + filterExpression.setLeft("source." + getter + "()"); + filterExpression.setRight(expression.getValue()); + result.addFilterExpressions(filterExpression); } } // 3. Find Entrance method of this metrics - Class c = metricsClass; + Class c = metricsClass; Method entranceMethod = null; SearchEntrance: while (!c.equals(Object.class)) { @@ -117,36 +94,17 @@ public class DeepAnalysis { entryMethod.addArg(parameterType, "1"); } else if (annotation instanceof org.apache.skywalking.oap.server.core.analysis.metrics.annotation.Expression) { if (result.getFuncConditionExpressions().size() == 1) { - ConditionExpression expression = result.getFuncConditionExpressions().get(0); + final ConditionExpression expression = result.getFuncConditionExpressions().get(0); + final FilterMatchers.MatcherInfo matcherInfo = FilterMatchers.INSTANCE.find(expression.getExpressionType()); - Expression argExpression = new Expression(); - if ("booleanMatch".equals(expression.getExpressionType())) { - argExpression.setExpressionObject("EqualMatch"); - argExpression.setLeft("source." + ClassMethodUtil.toIsMethod(expression.getAttribute()) + "()"); - argExpression.setRight(expression.getValue()); - } else if ("stringMatch".equals(expression.getExpressionType())) { - argExpression.setExpressionObject("EqualMatch"); - argExpression.setLeft("source." + ClassMethodUtil.toGetMethod(expression.getAttribute()) + "()"); - argExpression.setRight(expression.getValue()); - } else if ("greaterMatch".equals(expression.getExpressionType())) { - argExpression.setExpressionObject("GreaterMatch"); - argExpression.setLeft("source." + ClassMethodUtil.toGetMethod(expression.getAttribute()) + "()"); - argExpression.setRight(expression.getValue()); - } else if ("lessMatch".equals(expression.getExpressionType())) { - argExpression.setExpressionObject("LessMatch"); - argExpression.setLeft("source." + ClassMethodUtil.toGetMethod(expression.getAttribute()) + "()"); - argExpression.setRight(expression.getValue()); - } else if ("greaterEqualMatch".equals(expression.getExpressionType())) { - argExpression.setExpressionObject("GreaterEqualMatch"); - argExpression.setLeft("source." + ClassMethodUtil.toGetMethod(expression.getAttribute()) + "()"); - argExpression.setRight(expression.getValue()); - } else if ("lessEqualMatch".equals(expression.getExpressionType())) { - argExpression.setExpressionObject("LessEqualMatch"); - argExpression.setLeft("source." + ClassMethodUtil.toGetMethod(expression.getAttribute()) + "()"); - argExpression.setRight(expression.getValue()); - } else { - throw new IllegalArgumentException("filter expression [" + expression.getExpressionType() + "] not found"); - } + final String getter = matcherInfo.isBooleanType() + ? ClassMethodUtil.toIsMethod(expression.getAttribute()) + : ClassMethodUtil.toGetMethod(expression.getAttribute()); + + final Expression argExpression = new Expression(); + argExpression.setRight(expression.getValue()); + argExpression.setExpressionObject(matcherInfo.getMatcher().getName()); + argExpression.setLeft("source." + getter + "()"); entryMethod.addArg(argExpression); } else { diff --git a/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/FilterMatchers.java b/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/FilterMatchers.java new file mode 100644 index 000000000..27e8ae1ac --- /dev/null +++ b/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/FilterMatchers.java @@ -0,0 +1,95 @@ +/* + * 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.oal.rt.parser; + +import com.google.common.reflect.ClassPath; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; +import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.BooleanValueFilterMatcher; +import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.FilterMatcher; + +@SuppressWarnings("UnstableApiUsage") +public enum FilterMatchers { + INSTANCE; + + FilterMatchers() { + try { + init(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private final Map matchersKeyedByType = new HashMap<>(); + + private void init() throws IOException { + final ClassPath classpath = ClassPath.from(FilterMatchers.class.getClassLoader()); + final Set classes = classpath.getTopLevelClassesRecursive("org.apache.skywalking"); + for (ClassPath.ClassInfo classInfo : classes) { + final Class clazz = classInfo.load(); + + final FilterMatcher plainFilterMatcher = clazz.getAnnotation(FilterMatcher.class); + final BooleanValueFilterMatcher booleanFilterMatcher = clazz.getAnnotation(BooleanValueFilterMatcher.class); + if (plainFilterMatcher != null && booleanFilterMatcher != null) { + throw new IllegalStateException( + "A matcher class can not be annotated with both @FilterMatcher and @BooleanValueFilterMatcher" + ); + } + + if (plainFilterMatcher != null) { + for (final String type : plainFilterMatcher.value()) { + matchersKeyedByType.put(type, new MatcherInfo(clazz, false)); + } + if (plainFilterMatcher.value().length == 0) { + final String defaultTypeName = StringUtils.uncapitalize(clazz.getSimpleName()); + matchersKeyedByType.put(defaultTypeName, new MatcherInfo(clazz, false)); + } + } + + if (booleanFilterMatcher != null) { + for (final String type : booleanFilterMatcher.value()) { + matchersKeyedByType.put(type, new MatcherInfo(clazz, true)); + } + if (booleanFilterMatcher.value().length == 0) { + final String defaultTypeName = StringUtils.uncapitalize(clazz.getSimpleName()); + matchersKeyedByType.put(defaultTypeName, new MatcherInfo(clazz, true)); + } + } + } + } + + public MatcherInfo find(final String type) { + if (!matchersKeyedByType.containsKey(type)) { + throw new IllegalArgumentException("filter expression [" + type + "] not found"); + } + return matchersKeyedByType.get(type); + } + + @Getter + @AllArgsConstructor + public static class MatcherInfo { + private final Class matcher; + private final boolean isBooleanType; + } +} diff --git a/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/MetricsHolder.java b/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/MetricsHolder.java index 05efde291..fc62590a1 100644 --- a/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/MetricsHolder.java +++ b/oap-server/oal-rt/src/main/java/org/apache/skywalking/oal/rt/parser/MetricsHolder.java @@ -23,13 +23,16 @@ import com.google.common.reflect.ClassPath; import java.io.IOException; import java.util.HashMap; import java.util.Map; +import lombok.SneakyThrows; import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics; import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.MetricsFunction; +@SuppressWarnings("UnstableApiUsage") public class MetricsHolder { - private static Map> REGISTER = new HashMap<>(); + private static final Map> REGISTER = new HashMap<>(); + private static volatile boolean INITIALIZED = false; - public static void init() throws IOException { + private static void init() throws IOException { ClassPath classpath = ClassPath.from(MetricsHolder.class.getClassLoader()); ImmutableSet classes = classpath.getTopLevelClassesRecursive("org.apache.skywalking"); for (ClassPath.ClassInfo classInfo : classes) { @@ -45,13 +48,16 @@ public class MetricsHolder { } } - public static Class find( - String functionName) { - String func = functionName; - Class metricsClass = REGISTER.get( - func); + @SneakyThrows + public static Class find(String functionName) { + if (!INITIALIZED) { + init(); + INITIALIZED = true; + } + + Class metricsClass = REGISTER.get(functionName); if (metricsClass == null) { - throw new IllegalArgumentException("Can't find metrics, " + func); + throw new IllegalArgumentException("Can't find metrics, " + functionName); } return metricsClass; } diff --git a/oap-server/oal-rt/src/main/resources/code-templates/dispatcher/doMetrics.ftl b/oap-server/oal-rt/src/main/resources/code-templates/dispatcher/doMetrics.ftl index 88f649474..45ee4c405 100644 --- a/oap-server/oal-rt/src/main/resources/code-templates/dispatcher/doMetrics.ftl +++ b/oap-server/oal-rt/src/main/resources/code-templates/dispatcher/doMetrics.ftl @@ -3,7 +3,7 @@ ${metricsClassPackage}${metricsName}Metrics metrics = new ${metricsClassPackage} <#if filterExpressions??> <#list filterExpressions as filterExpression> - if (!new org.apache.skywalking.oap.server.core.analysis.metrics.expression.${filterExpression.expressionObject}().match(${filterExpression.left}, ${filterExpression.right})) { + if (!new ${filterExpression.expressionObject}().match(${filterExpression.left}, ${filterExpression.right})) { return; } @@ -18,7 +18,7 @@ metrics.${entryMethod.methodName}( <#if entryMethod.argTypes[arg_index] < 3> ${arg} <#else> - new org.apache.skywalking.oap.server.core.analysis.metrics.expression.${arg.expressionObject}().match(${arg.left}, ${arg.right}) + new ${arg.expressionObject}().match(${arg.left}, ${arg.right}) <#if arg_has_next>, ); diff --git a/oap-server/oal-rt/src/test/java/org/apache/skywalking/oal/rt/parser/DeepAnalysisTest.java b/oap-server/oal-rt/src/test/java/org/apache/skywalking/oal/rt/parser/DeepAnalysisTest.java index c86912521..e745ac10d 100644 --- a/oap-server/oal-rt/src/test/java/org/apache/skywalking/oal/rt/parser/DeepAnalysisTest.java +++ b/oap-server/oal-rt/src/test/java/org/apache/skywalking/oal/rt/parser/DeepAnalysisTest.java @@ -20,6 +20,10 @@ package org.apache.skywalking.oal.rt.parser; import java.io.IOException; import java.util.List; +import org.apache.skywalking.oap.server.core.analysis.metrics.expression.BooleanMatch; +import org.apache.skywalking.oap.server.core.analysis.metrics.expression.BooleanNotEqualMatch; +import org.apache.skywalking.oap.server.core.analysis.metrics.expression.EqualMatch; +import org.apache.skywalking.oap.server.core.analysis.metrics.expression.NotEqualMatch; import org.apache.skywalking.oap.server.core.annotation.AnnotationScan; import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine; import org.apache.skywalking.oap.server.core.storage.StorageException; @@ -28,14 +32,15 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + public class DeepAnalysisTest { @BeforeClass public static void init() throws IOException, StorageException { AnnotationScan scopeScan = new AnnotationScan(); scopeScan.registerListener(new DefaultScopeDefine.Listener()); scopeScan.scan(); - - MetricsHolder.init(); } @AfterClass @@ -122,8 +127,53 @@ public class DeepAnalysisTest { List filterExpressions = result.getFilterExpressions(); Assert.assertEquals(1, filterExpressions.size()); Expression filterExpression = filterExpressions.get(0); - Assert.assertEquals("EqualMatch", filterExpression.getExpressionObject()); + Assert.assertEquals(EqualMatch.class.getName(), filterExpression.getExpressionObject()); Assert.assertEquals("source.getName()", filterExpression.getLeft()); Assert.assertEquals("\"/service/prod/save\"", filterExpression.getRight()); } + + @Test + public void shouldUseCorrectMatcher() { + + AnalysisResult result = new AnalysisResult(); + result.setSourceName("Endpoint"); + result.setPackageName("endpoint.endpointavg"); + result.setSourceAttribute("latency"); + result.setMetricsName("EndpointAvg"); + result.setAggregationFunctionName("longAvg"); + + DeepAnalysis analysis = new DeepAnalysis(); + + result.setFilterExpressions(null); + result.setFilterExpressionsParserResult(null); + result.addFilterExpressionsParserResult(new ConditionExpression("booleanMatch", "valid", "")); + result = analysis.analysis(result); + assertTrue(result.getFilterExpressions().size() > 0); + assertEquals(BooleanMatch.class.getName(), result.getFilterExpressions().get(0).getExpressionObject()); + assertEquals("source.isValid()", result.getFilterExpressions().get(0).getLeft()); + + result.setFilterExpressions(null); + result.setFilterExpressionsParserResult(null); + result.addFilterExpressionsParserResult(new ConditionExpression("stringMatch", "type", "")); + result = analysis.analysis(result); + assertTrue(result.getFilterExpressions().size() > 0); + assertEquals(EqualMatch.class.getName(), result.getFilterExpressions().get(0).getExpressionObject()); + assertEquals("source.getType()", result.getFilterExpressions().get(0).getLeft()); + + result.setFilterExpressions(null); + result.setFilterExpressionsParserResult(null); + result.addFilterExpressionsParserResult(new ConditionExpression("notEqualMatch", "type", "")); + result = analysis.analysis(result); + assertTrue(result.getFilterExpressions().size() > 0); + assertEquals(NotEqualMatch.class.getName(), result.getFilterExpressions().get(0).getExpressionObject()); + assertEquals("source.getType()", result.getFilterExpressions().get(0).getLeft()); + + result.setFilterExpressions(null); + result.setFilterExpressionsParserResult(null); + result.addFilterExpressionsParserResult(new ConditionExpression("booleanNotEqualMatch", "type", "")); + result = analysis.analysis(result); + assertTrue(result.getFilterExpressions().size() > 0); + assertEquals(BooleanNotEqualMatch.class.getName(), result.getFilterExpressions().get(0).getExpressionObject()); + assertEquals("source.isType()", result.getFilterExpressions().get(0).getLeft()); + } } diff --git a/oap-server/oal-rt/src/test/java/org/apache/skywalking/oal/rt/parser/ScriptParserTest.java b/oap-server/oal-rt/src/test/java/org/apache/skywalking/oal/rt/parser/ScriptParserTest.java index b2eb37175..a066547fe 100644 --- a/oap-server/oal-rt/src/test/java/org/apache/skywalking/oal/rt/parser/ScriptParserTest.java +++ b/oap-server/oal-rt/src/test/java/org/apache/skywalking/oal/rt/parser/ScriptParserTest.java @@ -34,8 +34,6 @@ public class ScriptParserTest { @BeforeClass public static void init() throws IOException, StorageException { - MetricsHolder.init(); - AnnotationScan scopeScan = new AnnotationScan(); scopeScan.registerListener(new DefaultScopeDefine.Listener()); scopeScan.scan(); diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/annotation/BooleanValueFilterMatcher.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/annotation/BooleanValueFilterMatcher.java new file mode 100644 index 000000000..be5520e9e --- /dev/null +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/annotation/BooleanValueFilterMatcher.java @@ -0,0 +1,37 @@ +/* + * 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.server.core.analysis.metrics.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Exactly the same functionalities as {@link FilterMatcher} except for the value type of this matcher is {@code + * boolean}. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface BooleanValueFilterMatcher { + /** + * @return see {@link FilterMatcher#value()}. + */ + String[] value() default {}; +} diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/annotation/FilterMatcher.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/annotation/FilterMatcher.java new file mode 100644 index 000000000..743a448cd --- /dev/null +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/annotation/FilterMatcher.java @@ -0,0 +1,40 @@ +/* + * 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.server.core.analysis.metrics.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.apache.skywalking.oap.server.core.analysis.metrics.expression.BooleanMatch; + +/** + * Classes annotated with {@code FilterMatcher} are processors of the expressions in {@code filter} of the OAL script. + * Take {@link BooleanMatch} as an example. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface FilterMatcher { + /** + * @return the operator name(s) defined in the .g4 files, such as {@code lessEqualMatch} and {@code notEqualMatch}, + * the default value is the name of the class annotated with {@link FilterMatcher}, with the first letter being + * lowercase. + */ + String[] value() default {}; +} diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/BooleanMatch.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/BooleanMatch.java new file mode 100644 index 000000000..ca7eb68d9 --- /dev/null +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/BooleanMatch.java @@ -0,0 +1,32 @@ +/* + * 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.server.core.analysis.metrics.expression; + +import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.BooleanValueFilterMatcher; + +@BooleanValueFilterMatcher +public class BooleanMatch { + public boolean match(Boolean left, Boolean right) { + return left == right; + } + + public boolean match(boolean left, boolean right) { + return left == right; + } +} diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/BooleanNotEqualMatch.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/BooleanNotEqualMatch.java new file mode 100644 index 000000000..d23ad7900 --- /dev/null +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/BooleanNotEqualMatch.java @@ -0,0 +1,32 @@ +/* + * 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.server.core.analysis.metrics.expression; + +import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.BooleanValueFilterMatcher; + +@BooleanValueFilterMatcher +public class BooleanNotEqualMatch { + public boolean match(Boolean left, Boolean right) { + return left != right; + } + + public boolean match(boolean left, boolean right) { + return left != right; + } +} diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/EqualMatch.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/EqualMatch.java index 952d4c548..46ce2ed29 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/EqualMatch.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/EqualMatch.java @@ -19,48 +19,10 @@ package org.apache.skywalking.oap.server.core.analysis.metrics.expression; import java.util.Objects; +import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.FilterMatcher; +@FilterMatcher("stringMatch") public class EqualMatch { - public boolean match(int left, int right) { - return left == right; - } - - public boolean match(long left, long right) { - return left == right; - } - - public boolean match(float left, float right) { - return left == right; - } - - public boolean match(double left, double right) { - return left == right; - } - - public boolean match(Integer left, Integer right) { - return Objects.equals(left, right); - } - - public boolean match(Long left, Long right) { - return Objects.equals(left, right); - } - - public boolean match(Float left, Float right) { - return Objects.equals(left, right); - } - - public boolean match(Double left, Double right) { - return Objects.equals(left, right); - } - - public boolean match(Boolean left, Boolean right) { - return left == right; - } - - public boolean match(boolean left, boolean right) { - return left == right; - } - public boolean match(Object left, Object right) { return Objects.equals(left, right); } diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/GreaterEqualMatch.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/GreaterEqualMatch.java index 58395919d..05126633b 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/GreaterEqualMatch.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/GreaterEqualMatch.java @@ -18,6 +18,9 @@ package org.apache.skywalking.oap.server.core.analysis.metrics.expression; +import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.FilterMatcher; + +@FilterMatcher public class GreaterEqualMatch { public boolean match(int left, int right) { return left >= right; diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/GreaterMatch.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/GreaterMatch.java index 1b8b479d7..6d077dc8e 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/GreaterMatch.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/GreaterMatch.java @@ -18,6 +18,9 @@ package org.apache.skywalking.oap.server.core.analysis.metrics.expression; +import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.FilterMatcher; + +@FilterMatcher public class GreaterMatch { public boolean match(int left, int right) { return left > right; diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LessEqualMatch.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LessEqualMatch.java index 95276c832..35782e900 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LessEqualMatch.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LessEqualMatch.java @@ -18,6 +18,9 @@ package org.apache.skywalking.oap.server.core.analysis.metrics.expression; +import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.FilterMatcher; + +@FilterMatcher public class LessEqualMatch { public boolean match(int left, int right) { return left <= right; diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LessMatch.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LessMatch.java index bfee3069e..62a0edb88 100644 --- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LessMatch.java +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LessMatch.java @@ -18,6 +18,9 @@ package org.apache.skywalking.oap.server.core.analysis.metrics.expression; +import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.FilterMatcher; + +@FilterMatcher public class LessMatch { public boolean match(int left, int right) { return left < right; diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LikeMatch.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LikeMatch.java new file mode 100644 index 000000000..30fbfc3e8 --- /dev/null +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LikeMatch.java @@ -0,0 +1,36 @@ +/* + * 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.server.core.analysis.metrics.expression; + +import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.FilterMatcher; + +@FilterMatcher +public class LikeMatch { + public boolean match(String left, String right) { + if (left == null || right == null) { + return false; + } + if (left.startsWith("%") && left.endsWith("%")) { // %keyword% + return right.contains(left.substring(1, left.length() - 1)); + } + return (left.startsWith("%") && right.endsWith(left.substring(1))) // %suffix + || (left.endsWith("%") && right.startsWith(left.substring(0, left.length() - 1))) // prefix% + ; + } +} diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/NotEqualMatch.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/NotEqualMatch.java new file mode 100644 index 000000000..6475bc525 --- /dev/null +++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/NotEqualMatch.java @@ -0,0 +1,29 @@ +/* + * 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.server.core.analysis.metrics.expression; + +import java.util.Objects; +import org.apache.skywalking.oap.server.core.analysis.metrics.annotation.FilterMatcher; + +@FilterMatcher +public class NotEqualMatch { + public boolean match(Object left, Object right) { + return !Objects.equals(left, right); + } +} diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LikeMatchTest.java b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LikeMatchTest.java new file mode 100644 index 000000000..ebd7a85c8 --- /dev/null +++ b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/metrics/expression/LikeMatchTest.java @@ -0,0 +1,36 @@ +/* + * 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.server.core.analysis.metrics.expression; + +import org.junit.Test; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + +public class LikeMatchTest { + @Test + public void testLike() { + assertTrue(new LikeMatch().match("%Black", "MaxBlack")); + assertTrue(new LikeMatch().match("Max%", "MaxBlack")); + assertTrue(new LikeMatch().match("%axBl%", "MaxBlack")); + + assertFalse(new LikeMatch().match("Max%", "CarolineChanning")); + assertFalse(new LikeMatch().match("%Max", "CarolineChanning")); + } +}