diff --git a/CHANGES.md b/CHANGES.md
index 368d722cb..fa4b5facf 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -77,6 +77,7 @@ Release Notes.
* Upgrade etcd cluster coordinator and dynamic configuration to v3.x.
* Configuration: Allow to configure server maximum request header size.
* Add thread state metric and class loaded info metric to JVMMetric.
+* Performance: compile LAL DSL statically and run with type checked.
#### UI
diff --git a/Makefile b/Makefile
index 4ec2a1e61..acbab49c0 100644
--- a/Makefile
+++ b/Makefile
@@ -96,7 +96,7 @@ docker.agent: $(SW_ROOT)/docker/agent/Dockerfile.agent
# 4. This rule runs $(BUILD_PRE) prior to any docker build and only if specified as a dependency variable
# 5. This rule finally runs docker build passing $(BUILD_ARGS) to docker if they are specified as a dependency variable
-DOCKER_RULE=time (mkdir -p $(DOCKER_BUILD_TOP)/$@ && cp -r $^ $(DOCKER_BUILD_TOP)/$@ && cd $(DOCKER_BUILD_TOP)/$@ && $(BUILD_PRE) docker build $(BUILD_ARGS) -t $(HUB)/$(subst docker.,,$@):$(TAG) -f Dockerfile$(suffix $@) .)
+DOCKER_RULE=time (mkdir -p $(DOCKER_BUILD_TOP)/$@ && cp -r $^ $(DOCKER_BUILD_TOP)/$@ && cd $(DOCKER_BUILD_TOP)/$@ && $(BUILD_PRE) docker build --no-cache $(BUILD_ARGS) -t $(HUB)/$(subst docker.,,$@):$(TAG) -f Dockerfile$(suffix $@) .)
# for each docker.XXX target create a push.docker.XXX target that pushes
# the local docker image to another hub
diff --git a/dist-material/release-docs/LICENSE b/dist-material/release-docs/LICENSE
index ecb5a6f46..e049a27f0 100755
--- a/dist-material/release-docs/LICENSE
+++ b/dist-material/release-docs/LICENSE
@@ -455,7 +455,7 @@ Apache 2.0 licenses
========================================
echarts 5.0.2: https://github.com/apache/echarts Apache-2.0
Material Icons 3.0.1 https://github.com/google/material-design-icons Apache-2.0
-groovy 3.0.3 https://github.com/apache/groovy Apache-2.0
+groovy 3.0.8 https://github.com/apache/groovy Apache-2.0
========================================
BSD licenses
diff --git a/oap-server-bom/pom.xml b/oap-server-bom/pom.xml
index 6fad449b8..993d301b6 100644
--- a/oap-server-bom/pom.xml
+++ b/oap-server-bom/pom.xml
@@ -68,7 +68,7 @@
2.3.28
3.25.0-GA
0.10.3
- 3.0.3
+ 3.0.8
2.4.8.Final
1.9.4
1.12.0
diff --git a/oap-server/analyzer/log-analyzer/pom.xml b/oap-server/analyzer/log-analyzer/pom.xml
index f1dd346da..fbe0e6ca9 100644
--- a/oap-server/analyzer/log-analyzer/pom.xml
+++ b/oap-server/analyzer/log-analyzer/pom.xml
@@ -42,6 +42,10 @@
org.codehaus.groovy
groovy
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
diff --git a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/DSL.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/DSL.java
index c5fad4e21..c7db4053b 100644
--- a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/DSL.java
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/DSL.java
@@ -19,14 +19,20 @@
package org.apache.skywalking.oap.log.analyzer.dsl;
import groovy.lang.GroovyShell;
+import groovy.transform.CompileStatic;
import groovy.util.DelegatingScript;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
+import org.apache.skywalking.oap.log.analyzer.dsl.spec.LALDelegatingScript;
import org.apache.skywalking.oap.log.analyzer.dsl.spec.filter.FilterSpec;
import org.apache.skywalking.oap.log.analyzer.provider.LogAnalyzerModuleConfig;
import org.apache.skywalking.oap.server.library.module.ModuleManager;
import org.apache.skywalking.oap.server.library.module.ModuleStartException;
import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer;
+
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class DSL {
@@ -38,7 +44,16 @@ public class DSL {
final LogAnalyzerModuleConfig config,
final String dsl) throws ModuleStartException {
final CompilerConfiguration cc = new CompilerConfiguration();
- cc.setScriptBaseClass(DelegatingScript.class.getName());
+ final ASTTransformationCustomizer customizer =
+ new ASTTransformationCustomizer(
+ singletonMap(
+ "extensions",
+ singletonList(LALPrecompiledExtension.class.getName())
+ ),
+ CompileStatic.class
+ );
+ cc.addCompilationCustomizers(customizer);
+ cc.setScriptBaseClass(LALDelegatingScript.class.getName());
final GroovyShell sh = new GroovyShell(cc);
final DelegatingScript script = (DelegatingScript) sh.parse(dsl);
diff --git a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/LALPrecompiledExtension.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/LALPrecompiledExtension.java
new file mode 100644
index 000000000..56888a980
--- /dev/null
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/LALPrecompiledExtension.java
@@ -0,0 +1,89 @@
+/*
+ * 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.log.analyzer.dsl;
+
+import org.apache.skywalking.apm.network.logging.v3.LogData;
+import org.apache.skywalking.apm.network.logging.v3.LogDataBody;
+import org.apache.skywalking.apm.network.logging.v3.LogTags;
+import org.apache.skywalking.apm.network.logging.v3.TraceContext;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.transform.stc.AbstractTypeCheckingExtension;
+import org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor;
+
+import static org.codehaus.groovy.ast.ClassHelper.makeCached;
+
+public class LALPrecompiledExtension extends AbstractTypeCheckingExtension {
+
+ public LALPrecompiledExtension(final StaticTypeCheckingVisitor typeCheckingVisitor) {
+ super(typeCheckingVisitor);
+ }
+
+ @Override
+ public boolean handleUnresolvedProperty(final PropertyExpression pexp) {
+ final Expression exp = pexp.getObjectExpression();
+
+ if (exp.getText().startsWith("parsed")) {
+ makeDynamic(pexp);
+ setHandled(true);
+ return true;
+ }
+
+ if (exp.getText().startsWith("log")) {
+ if (handleLogVariable(pexp)) {
+ return true;
+ }
+ }
+
+ return super.handleUnresolvedProperty(pexp);
+ }
+
+ private boolean handleLogVariable(final PropertyExpression pexp) {
+ final Expression exp = pexp.getObjectExpression();
+ final Expression p = pexp.getProperty();
+
+ if (exp instanceof VariableExpression) {
+ final VariableExpression v = (VariableExpression) exp;
+ if (v.getName().equals("log")) {
+ storeType(v, makeCached(LogData.Builder.class));
+ }
+ if (p instanceof ConstantExpression) {
+ final ConstantExpression c = (ConstantExpression) p;
+ switch (c.getText()) {
+ case "body":
+ storeType(pexp, makeCached(LogDataBody.class));
+ break;
+ case "traceContext":
+ storeType(pexp, makeCached(TraceContext.class));
+ break;
+ case "tags":
+ storeType(pexp, makeCached(LogTags.class));
+ break;
+ }
+ }
+ setHandled(true);
+ return true;
+ }
+
+ return false;
+ }
+}
+
diff --git a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/LALDelegatingScript.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/LALDelegatingScript.java
new file mode 100644
index 000000000..a057373ef
--- /dev/null
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/LALDelegatingScript.java
@@ -0,0 +1,56 @@
+/*
+ * 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.log.analyzer.dsl.spec;
+
+import groovy.lang.Closure;
+import groovy.lang.DelegatesTo;
+import groovy.util.DelegatingScript;
+import org.apache.skywalking.oap.log.analyzer.dsl.spec.filter.FilterSpec;
+
+public class LALDelegatingScript extends DelegatingScript {
+ @Override
+ public Object run() {
+ return null;
+ }
+
+ public void filter(@DelegatesTo(value = FilterSpec.class, strategy = Closure.DELEGATE_ONLY) Closure> closure) {
+ closure.setDelegate(getDelegate());
+ closure.call();
+ }
+
+ public void json(@DelegatesTo(value = FilterSpec.class) Closure> closure) {
+ closure.setDelegate(getDelegate());
+ closure.call();
+ }
+
+ public void text(@DelegatesTo(value = FilterSpec.class) Closure> closure) {
+ closure.setDelegate(getDelegate());
+ closure.call();
+ }
+
+ public void extractor(@DelegatesTo(value = FilterSpec.class) Closure> closure) {
+ closure.setDelegate(getDelegate());
+ closure.call();
+ }
+
+ public void sink(@DelegatesTo(value = FilterSpec.class) Closure> closure) {
+ closure.setDelegate(getDelegate());
+ closure.call();
+ }
+}
diff --git a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/extractor/ExtractorSpec.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/extractor/ExtractorSpec.java
index 49ea7d607..c51f445ff 100644
--- a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/extractor/ExtractorSpec.java
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/extractor/ExtractorSpec.java
@@ -21,6 +21,7 @@ package org.apache.skywalking.oap.log.analyzer.dsl.spec.extractor;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import groovy.lang.Closure;
+import groovy.lang.DelegatesTo;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -54,7 +55,8 @@ public class ExtractorSpec extends AbstractSpec {
final LogAnalyzerModuleConfig moduleConfig) throws ModuleStartException {
super(moduleManager, moduleConfig);
- final MeterSystem meterSystem = moduleManager.find(CoreModule.NAME).provider().getService(MeterSystem.class);
+ final MeterSystem meterSystem =
+ moduleManager.find(CoreModule.NAME).provider().getService(MeterSystem.class);
metricConverts = moduleConfig.malConfigs()
.stream()
@@ -93,7 +95,7 @@ public class ExtractorSpec extends AbstractSpec {
}
@SuppressWarnings("unused")
- public void tag(final Map kv) {
+ public void tag(final Map kv) {
if (BINDING.get().shouldAbort()) {
return;
}
@@ -108,7 +110,8 @@ public class ExtractorSpec extends AbstractSpec {
kv.entrySet()
.stream()
.filter(it -> isNotBlank(it.getKey()))
- .filter(it -> nonNull(it.getValue()) && isNotBlank(Objects.toString(it.getValue())))
+ .filter(it -> nonNull(it.getValue()) &&
+ isNotBlank(Objects.toString(it.getValue())))
.map(it -> {
final Object val = it.getValue();
String valStr = Objects.toString(val);
@@ -176,7 +179,7 @@ public class ExtractorSpec extends AbstractSpec {
}
@SuppressWarnings("unused")
- public void metrics(final Closure cl) {
+ public void metrics(@DelegatesTo(SampleBuilder.class) final Closure> cl) {
if (BINDING.get().shouldAbort()) {
return;
}
@@ -188,8 +191,8 @@ public class ExtractorSpec extends AbstractSpec {
metricConverts.forEach(it -> it.toMeter(
ImmutableMap.builder()
- .put(sample.getName(), SampleFamilyBuilder.newBuilder(sample).build())
- .build()
+ .put(sample.getName(), SampleFamilyBuilder.newBuilder(sample).build())
+ .build()
));
}
@@ -198,11 +201,15 @@ public class ExtractorSpec extends AbstractSpec {
private final Sample.SampleBuilder sampleBuilder = Sample.builder();
@SuppressWarnings("unused")
- public Sample.SampleBuilder labels(final Map labels) {
- final Map filtered = labels.entrySet()
- .stream()
- .filter(it -> isNotBlank(it.getKey()) && isNotBlank(it.getValue()))
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ public Sample.SampleBuilder labels(final Map labels) {
+ final Map filtered =
+ labels.entrySet()
+ .stream()
+ .filter(it -> isNotBlank(it.getKey()) && nonNull(it.getValue()))
+ .collect(
+ Collectors.toMap(Map.Entry::getKey,
+ it -> Objects.toString(it.getValue()))
+ );
return sampleBuilder.labels(ImmutableMap.copyOf(filtered));
}
}
diff --git a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/filter/FilterSpec.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/filter/FilterSpec.java
index 7683bb63e..43f86a360 100644
--- a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/filter/FilterSpec.java
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/filter/FilterSpec.java
@@ -18,10 +18,10 @@
package org.apache.skywalking.oap.log.analyzer.dsl.spec.filter;
-import com.google.gson.reflect.TypeToken;
+import com.fasterxml.jackson.core.type.TypeReference;
import com.google.protobuf.TextFormat;
import groovy.lang.Closure;
-import java.lang.reflect.Type;
+import groovy.lang.DelegatesTo;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -57,14 +57,14 @@ public class FilterSpec extends AbstractSpec {
private final SinkSpec sink;
- private final Type parsedType;
+ private final TypeReference