This is an automated email from the ASF dual-hosted git repository. wusheng pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/skywalking-graalvm-distro.git
commit dc6f5e1833cd87f58fdc23370edf84b53358e850 Author: Wu Sheng <[email protected]> AuthorDate: Sat Feb 21 19:56:50 2026 +0800 Runtime wiring: replace Groovy with pure Java MalExpression/MalFilter at runtime Wire transpiled MAL classes into runtime classpath: - Expression.java: wraps MalExpression instead of Groovy DelegatingScript - DSL.java: loads from mal-expressions.txt manifest with MalExpression classes - FilterExpression.java: loads from mal-filter-expressions.properties with MalFilter - Add Expression to shade excludes so same-FQCN replacement takes effect - Add InstanceEntityDescription same-FQCN replacement - Add native-image profile and Makefile targets (native-image, trace-agent) - Update MAL/LAL immigration docs with Phase 3 transpiler plans --- LAL-IMMIGRATION.md | 81 ++++- MAL-IMMIGRATION.md | 368 ++++++++++++++++++++- Makefile | 14 +- build-tools/precompiler/MAL-TRANSPILER-DESIGN.md | 219 ++++++++++++ oap-graalvm-native/pom.xml | 37 +++ .../meter-analyzer-for-graalvm/pom.xml | 2 + .../skywalking/oap/meter/analyzer/dsl/DSL.java | 44 ++- .../InstanceEntityDescription.java | 51 +++ .../oap/meter/analyzer/dsl/Expression.java | 90 +++++ .../oap/meter/analyzer/dsl/FilterExpression.java | 38 +-- pom.xml | 1 + 11 files changed, 896 insertions(+), 49 deletions(-) diff --git a/LAL-IMMIGRATION.md b/LAL-IMMIGRATION.md index e6c18cc..6b54950 100644 --- a/LAL-IMMIGRATION.md +++ b/LAL-IMMIGRATION.md @@ -1,4 +1,4 @@ -# Phase 2: LAL Build-Time Pre-Compilation — COMPLETE +# LAL Immigration: Build-Time Pre-Compilation + Groovy Elimination ## Context @@ -218,3 +218,82 @@ JAVA_HOME=/Users/wusheng/.sdkman/candidates/java/25-graal make build-distro ``` Expected: 19 comparison tests across 5 test classes, all passing. + +--- + +# Phase 3: Eliminate Groovy — Pure Java LAL Transpiler (Deferred) + +## Context + +LAL uses `@CompileStatic` Groovy, which generates standard Java bytecode without +dynamic MOP. However, the Groovy runtime library is still required at runtime for +base classes (`DelegatingScript`, `Closure`, `Binding`, `Script`). Since MAL's +Phase 3 eliminates Groovy from runtime entirely, LAL must follow the same approach +to fully remove the Groovy dependency. + +**Priority**: LAL transpilation is deferred until after MAL transpilation is complete +and validated. There are only 10 LAL scripts (6 unique after SHA-256 dedup), compared +to 1250+ MAL expressions. LAL can be tackled separately. + +--- + +## Approach + +The same transpiler pattern used for MAL applies to LAL, but LAL scripts are more +complex — they are full programs with nested spec calls rather than method chains: + +```groovy +filter { + if (tag("LOG_KIND") == "NET_PROFILING_SAMPLED_TRACE") { + json { + tag("address", parsed.address) + } + sampledTrace { + // ... + } + } else { + abort {} + } +} +``` + +### Option A (Recommended): Transpile LAL to Java + +Generate pure Java classes that implement the same logic: + +```java +public class LalScript_envoy_als implements LalExpression { + @Override + public void run(FilterSpec filterSpec, Binding binding) { + filterSpec.filter(() -> { + if ("NET_PROFILING_SAMPLED_TRACE".equals(binding.getTag("LOG_KIND"))) { + filterSpec.json(() -> { + binding.tag("address", binding.getParsed().get("address")); + }); + filterSpec.sampledTrace(() -> { ... }); + } else { + filterSpec.abort(); + } + }); + } +} +``` + +### Option B: Minimal Groovy Stubs + +Keep LAL as `@CompileStatic` pre-compiled bytecode but provide minimal stubs for +the few Groovy base classes needed (`DelegatingScript`, `Closure`, `Binding`). +This avoids the full Groovy runtime while keeping the pre-compiled bytecode working. + +--- + +## Implementation (when ready) + +1. Create `LalExpression` interface +2. Create `LalToJavaTranspiler` (similar to `MalToJavaTranspiler`) +3. Handle LAL-specific patterns: `filter {}`, `json {}`, `text { regexp }`, + `extractor {}`, `sink {}`, `sampledTrace {}`, `slowSql {}`, `abort {}` +4. Replace LAL `DSL.java` to load `LalExpression` instead of `DelegatingScript` +5. Update precompiler `compileLAL()` to use transpiler +6. Run comparison tests (19 tests across 5 classes) +7. Remove Groovy from runtime classpath diff --git a/MAL-IMMIGRATION.md b/MAL-IMMIGRATION.md index 499df5a..99ae638 100644 --- a/MAL-IMMIGRATION.md +++ b/MAL-IMMIGRATION.md @@ -1,4 +1,4 @@ -# Phase 2: MAL/LAL Build-Time Pre-Compilation — COMPLETE +# MAL Immigration: Build-Time Pre-Compilation + Groovy Elimination ## Context @@ -456,3 +456,369 @@ cat build-tools/oal-exporter/target/generated-oal-classes/META-INF/annotation-sc # 6. Verify tests pass make build-distro # runs MALCompilerTest + MALPrecompiledRegistrationTest ``` + +--- + +# Phase 3: Eliminate Groovy — Pure Java MAL Transpiler + +## Why: Groovy + GraalVM Native Image is Incompatible + +The first native-image build (`make native-image`) failed with: + +``` +Error: Could not find target method: + protected static void com.oracle.svm.polyglot.groovy + .Target_org_codehaus_groovy_vmplugin_v7_IndyInterface_invalidateSwitchPoints + .invalidateSwitchPoints() +``` + +**Root cause**: GraalVM's built-in `GroovyIndyInterfaceFeature` (in `library-support.jar`) +targets `IndyInterface.invalidateSwitchPoints()` — a method removed in Groovy 5.0.3. +The Groovy 5.x runtime still uses `org.codehaus.groovy` packages and `invokedynamic` +bootstrapping, but the internal API changed. GraalVM's substitution layer cannot find +the target method. + +**Why fixing Groovy is not worth it**: Even if the substitution were patched, Groovy's +dynamic runtime (ExpandoMetaClass, MOP, IndyInterface, MetaClassRegistry) requires +extensive GraalVM reflection/substitution configuration. The MAL pre-compiled scripts +use `invokedynamic` + Groovy MOP for method resolution. Making this work reliably in +native-image would be fragile and complex. + +**Solution**: Eliminate Groovy from runtime entirely. Generate pure Java code at build +time that implements the same MAL expression logic. The existing 1303 UTs validate +that the generated Java code produces identical results to the Groovy-compiled scripts. + +--- + +## Approach: MAL-to-Java Transpiler + +**Key insight**: MAL expressions are method chains on `SampleFamily` with a well-defined +API. The precompiler already parses all 1250+ MAL rules. Instead of compiling them to +Groovy bytecode (which needs Groovy runtime), we parse the Groovy AST at build time and +generate equivalent Java source code. Zero Groovy at runtime. + +### What Changes + +| Aspect | Phase 2 (Groovy pre-compilation) | Phase 3 (Java transpiler) | +|--------|----------------------------------|--------------------------| +| Build output | Groovy `.class` bytecode | Pure Java `.class` files | +| Runtime dependency | Groovy runtime (MOP, ExpandoMetaClass) | None | +| Expression execution | `DelegatingScript.run()` + `ExpandoMetaClass` | `MalExpression.run(Map<String, SampleFamily>)` | +| Closure parameters | `groovy.lang.Closure` | Java functional interfaces | +| `propertyMissing()` | Groovy MOP resolves metric names | `samples.get("metricName")` | +| `Number * SampleFamily` | `ExpandoMetaClass` on `Number` | `sf.multiply(number)` (transpiler swaps operands) | +| Filter expressions | Groovy `Script.run()` → `Closure<Boolean>` | `SampleFilter.test(Map<String, String>)` | + +### What Stays the Same + +- **MeterSystem Javassist generation**: Pre-compiled at build time (Phase 2), unchanged +- **Config data serialization**: JSON manifests for rule configs, unchanged +- **Manifest-based loading**: Same pattern, new manifest format for Java expressions +- **MetricConvert pipeline**: Still runs at build time for Javassist class generation + +--- + +## Step 1: Java Functional Interfaces for Closure Replacements + +5 `SampleFamily` methods accept `groovy.lang.Closure` parameters. Replace with Java +functional interfaces: + +| Method | Closure Type | Java Interface | +|--------|-------------|----------------| +| `tag(Closure<?> cl)` | `Map<String,String> → Map<String,String>` | `TagFunction extends Function<Map, Map>` | +| `filter(Closure<Boolean> f)` | `Map<String,String> → Boolean` | `SampleFilter extends Predicate<Map>` | +| `forEach(List, Closure each)` | `(String, Map) → void` | `ForEachFunction extends BiConsumer<String, Map>` | +| `decorate(Closure c)` | `MeterEntity → void` | `DecorateFunction extends Consumer<MeterEntity>` | +| `instance(..., Closure extractor)` | `Map<String,String> → Map<String,String>` | `PropertiesExtractor extends Function<Map, Map>` | + +**Files created**: +- `oap-libs-for-graalvm/meter-analyzer-for-graalvm/.../dsl/MalExpression.java` +- `oap-libs-for-graalvm/meter-analyzer-for-graalvm/.../dsl/SampleFamilyFunctions.java` + +### MalExpression Interface + +```java +public interface MalExpression { + SampleFamily run(Map<String, SampleFamily> samples); +} +``` + +Each generated MAL expression class implements this interface. The `samples` map +replaces Groovy's `propertyMissing()` delegate — metric names are resolved via +`samples.get("metricName")`. + +### MalFilter Interface + +```java +public interface MalFilter { + boolean test(Map<String, String> tags); +} +``` + +Each generated filter expression class implements this interface. + +--- + +## Step 2: Same-FQCN SampleFamily Replacement + +**File**: `oap-libs-for-graalvm/meter-analyzer-for-graalvm/.../dsl/SampleFamily.java` + +Copy upstream `SampleFamily.java` and change ONLY the 5 Closure-based method signatures: + +```java +// Before (upstream) +public SampleFamily tag(Closure<?> cl) { ... cl.rehydrate(...).call(arg) ... } +public SampleFamily filter(Closure<Boolean> filter) { ... filter.call(it.labels) ... } +public SampleFamily forEach(List<String> array, Closure<Void> each) { ... each.call(element, labels) ... } +public SampleFamily decorate(Closure<Void> c) { ... c.call(meterEntity) ... } +public SampleFamily instance(..., Closure<Map<String,String>> propertiesExtractor) { ... } + +// After (replacement) +public SampleFamily tag(TagFunction fn) { ... fn.apply(arg) ... } +public SampleFamily filter(SampleFilter f) { ... f.test(it.labels) ... } +public SampleFamily forEach(List<String> array, ForEachFunction each) { ... each.accept(element, labels) ... } +public SampleFamily decorate(DecorateFunction fn) { ... fn.accept(meterEntity) ... } +public SampleFamily instance(..., PropertiesExtractor extractor) { ... } +``` + +All other methods (tagEqual, sum, avg, histogram, service, endpoint, etc.) remain +identical — they don't use Groovy types. + +**Shade config**: Add `SampleFamily.class` and `SampleFamily$*.class` to shade excludes. + +--- + +## Step 3: MAL-to-Java Transpiler + +**File**: `build-tools/precompiler/.../MalToJavaTranspiler.java` + +Parses MAL expression strings using Groovy's parser (available at build time, not needed +at runtime) and generates pure Java source code. + +### Input → Output Example + +**Input** (MAL expression string from apisix.yaml): +``` +apisix_http_status.tagNotEqual('route','').tag({tags->tags.route='route/'+tags['route']}) + .sum(['code','service_name','route','node']).rate('PT1M').endpoint(['service_name'],['route'], Layer.APISIX) +``` + +**Output** (generated Java class): +```java +package org.apache.skywalking.oap.server.core.source.oal.rt.mal; + +import java.util.*; +import org.apache.skywalking.oap.meter.analyzer.dsl.*; +import org.apache.skywalking.oap.server.core.analysis.Layer; + +public class MalExpr_meter_apisix_endpoint_http_status implements MalExpression { + @Override + public SampleFamily run(Map<String, SampleFamily> samples) { + SampleFamily v0 = samples.get("apisix_http_status"); + if (v0 == null) return SampleFamily.EMPTY; + return v0 + .tagNotEqual("route", "") + .tag(tags -> { tags.put("route", "route/" + tags.get("route")); return tags; }) + .sum(List.of("code", "service_name", "route", "node")) + .rate("PT1M") + .endpoint(List.of("service_name"), List.of("route"), Layer.APISIX); + } +} +``` + +### AST Node → Java Output Mapping + +| Groovy AST Node | Java Output | +|-----------------|-------------| +| Bare property access (`metric_name`) | `samples.get("metric_name")` | +| Method call on SampleFamily | Direct method call (same name) | +| `Closure` in `.tag()` | Lambda: `tags -> { tags.put(...); return tags; }` | +| `Closure` in `.filter()` | Lambda: `labels -> booleanExpr` | +| `Closure` in `.forEach()` | Lambda: `(element, labels) -> { ... }` | +| `tags.name` inside closure | `tags.get("name")` | +| `tags.name = val` inside closure | `tags.put("name", val)` | +| `tags['key']` inside closure | `tags.get("key")` | +| Binary `SF + SF` | `left.plus(right)` | +| Binary `SF - SF` | `left.minus(right)` | +| Binary `SF * SF` | `left.multiply(right)` | +| Binary `SF / SF` | `left.div(right)` | +| Binary `Number * SF` | `sf.multiply(number)` (swap operands) | +| Binary `Number + SF` | `sf.plus(number)` (swap operands) | +| Binary `Number - SF` | `sf.minus(number).negative()` (swap operands) | +| Binary `Number / SF` | `sf.newValue(v -> number / v)` (swap operands) | +| Unary `-SF` | `sf.negative()` | +| `['a','b','c']` (list literal) | `List.of("a", "b", "c")` | +| `[50,75,90,95,99]` (int list) | `List.of(50, 75, 90, 95, 99)` | +| String literal `'foo'` | `"foo"` | +| Enum constant `Layer.APISIX` | `Layer.APISIX` | +| Enum constant `DetectPoint.SERVER` | `DetectPoint.SERVER` | +| `DownsamplingType.LATEST` / `LATEST` | `DownsamplingType.LATEST` | +| `time()` function | `java.time.Instant.now().getEpochSecond()` | +| `a?.b` (safe navigation) | `a != null ? a.b : null` | +| `a ?: b` (elvis) | `a != null ? a : b` | + +### Filter Expression Transpilation + +Filter expressions are simpler — they are boolean closures: + +**Input**: `{ tags -> tags.job_name == 'apisix-monitoring' }` + +**Output**: +```java +public class MalFilter_apisix implements MalFilter { + @Override + public boolean test(Map<String, String> tags) { + return "apisix-monitoring".equals(tags.get("job_name")); + } +} +``` + +--- + +## Step 4: Same-FQCN Expression.java Replacement + +**File**: `oap-libs-for-graalvm/meter-analyzer-for-graalvm/.../dsl/Expression.java` + +Simplified — no DelegatingScript, no ExpandoMetaClass, no ExpressionDelegate: + +```java +public class Expression { + private final String metricName; + private final String literal; + private final MalExpression expression; // Pure Java + + public Result run(Map<String, SampleFamily> sampleFamilies) { + SampleFamily sf = expression.run(sampleFamilies); + if (sf == SampleFamily.EMPTY) return Result.fail("EMPTY"); + return Result.success(sf); + } + + // parse() still provides ExpressionParsingContext for static analysis + // (scopeType, functionName, etc.) — this is unchanged +} +``` + +--- + +## Step 5: Update DSL.java and FilterExpression.java + +### DSL.java (already exists, update) + +Change from loading `DelegatingScript` to loading `MalExpression`: + +```java +public static Expression parse(String metricName, String expression) { + MalExpression malExpr = loadFromManifest(metricName); // Class.forName + newInstance + return new Expression(metricName, expression, malExpr); +} +``` + +Manifest: `META-INF/mal-expressions.txt` (replaces `mal-groovy-scripts.txt`): +``` +meter_apisix_endpoint_http_status=org.apache.skywalking.oap.server.core.source.oal.rt.mal.MalExpr_meter_apisix_endpoint_http_status +``` + +### FilterExpression.java (already exists, update) + +Change from loading Groovy `Script` → `Closure<Boolean>` to loading `MalFilter`: + +```java +public class FilterExpression { + private final MalFilter filter; + + public FilterExpression(String literal) { + this.filter = loadFromManifest(literal); // Class.forName + newInstance + } + + public Map<String, SampleFamily> filter(Map<String, SampleFamily> sampleFamilies) { + // Apply filter.test(labels) to each sample family + } +} +``` + +--- + +## Step 6: Update Precompiler + +Replace `compileMAL()` in `Precompiler.java` to use the transpiler instead of Groovy +compilation: + +```java +private static void compileMAL(String outputDir, ...) { + MalToJavaTranspiler transpiler = new MalToJavaTranspiler(outputDir); + + for (Rule rule : allRules) { + for (MetricsRule mr : rule.getMetricsRules()) { + String metricName = formatMetricName(rule, mr.getName()); + String expression = formatExp(rule.getExpPrefix(), rule.getExpSuffix(), mr.getExp()); + transpiler.transpile(metricName, expression); + } + if (rule.getFilter() != null) { + transpiler.transpileFilter(rule.getName(), rule.getFilter()); + } + } + + transpiler.writeManifest(); // mal-expressions.txt + transpiler.compileAll(); // javac on generated .java files +} +``` + +The Javassist meter class generation is still run via `MetricConvert` pipeline (unchanged +from Phase 2). Only Groovy compilation is replaced by transpilation. + +--- + +## Same-FQCN Replacements (Phase 3 — additions/updates) + +| Upstream Class | Replacement Location | Phase 3 Change | +|---|---|---| +| `SampleFamily` | `meter-analyzer-for-graalvm/` | **New.** Closure parameters → functional interfaces | +| `Expression` | `meter-analyzer-for-graalvm/` | **New.** No Groovy: uses `MalExpression` instead of `DelegatingScript` | +| `DSL` (MAL) | `meter-analyzer-for-graalvm/` | **Updated.** Loads `MalExpression` instead of Groovy `DelegatingScript` | +| `FilterExpression` | `meter-analyzer-for-graalvm/` | **Updated.** Loads `MalFilter` instead of Groovy `Closure<Boolean>` | + +--- + +## Files Created (Phase 3) + +| File | Purpose | +|------|---------| +| `.../dsl/MalExpression.java` | Interface for generated MAL expression classes | +| `.../dsl/MalFilter.java` | Interface for generated MAL filter classes | +| `.../dsl/SampleFamilyFunctions.java` | Functional interfaces: TagFunction, SampleFilter, ForEachFunction, DecorateFunction, PropertiesExtractor | +| `.../meter-analyzer-for-graalvm/SampleFamily.java` | Same-FQCN: Closure → functional interfaces | +| `.../meter-analyzer-for-graalvm/Expression.java` | Same-FQCN: no Groovy, uses MalExpression | +| `.../precompiler/MalToJavaTranspiler.java` | AST-walking transpiler: Groovy AST → Java source | +| Generated `MalExpr_*.java` files | ~1250 pure Java expression classes | +| Generated `MalFilter_*.java` files | ~29 pure Java filter classes | + +## Files Modified (Phase 3) + +| File | Change | +|------|--------| +| `Precompiler.java` | `compileMAL()` uses transpiler instead of Groovy compilation | +| `meter-analyzer-for-graalvm/pom.xml` | Add SampleFamily, Expression to shade excludes | +| `meter-analyzer-for-graalvm/DSL.java` | Load MalExpression instead of DelegatingScript | +| `meter-analyzer-for-graalvm/FilterExpression.java` | Load MalFilter instead of Groovy Closure | + +--- + +## Verification (Phase 3) + +```bash +# 1. Rebuild with transpiler +make compile + +# 2. Run ALL existing tests (1303 UTs validate identical MAL behavior) +make test + +# 3. Boot JVM distro +make shutdown && make boot + +# 4. Verify no Groovy at runtime (after LAL is also transpiled) +jar tf oap-graalvm-server/target/oap-graalvm-jvm-distro/oap-graalvm-jvm-distro/libs/*.jar | grep groovy +# Should find NOTHING + +# 5. Attempt native-image build (no more Groovy substitution error) +make native-image +``` diff --git a/Makefile b/Makefile index 27430d3..1ef37aa 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ SW_VERSION := $(shell grep '<revision>' skywalking/pom.xml | head -1 | sed 's/.* MVN := ./mvnw MVN_ARGS := -Dskywalking.version=$(SW_VERSION) -.PHONY: all clean build init-submodules build-skywalking build-distro compile test javadoc dist info docker-up docker-down boot shutdown +.PHONY: all clean build init-submodules build-skywalking build-distro compile test javadoc dist info docker-up docker-down boot shutdown native-image trace-agent all: build @@ -85,6 +85,18 @@ docker-down: shutdown: @oap-graalvm-server/target/oap-graalvm-jvm-distro/oap-graalvm-jvm-distro/bin/oapServiceStop.sh 2>/dev/null || true +# Build native image (requires GraalVM JDK) +native-image: compile + $(MVN) package -pl oap-graalvm-native -Pnative -DskipTests $(MVN_ARGS) + +# Run tracing agent to capture supplementary native-image metadata +# Merges with pre-generated reflect-config.json from the precompiler +trace-agent: build-distro docker-up + SW_STORAGE_BANYANDB_TARGETS=localhost:17912 \ + SW_CLUSTER=standalone \ + JAVA_OPTS="-Xms256M -Xmx4096M -agentlib:native-image-agent=config-merge-dir=oap-graalvm-native/src/main/resources/META-INF/native-image/org.apache.skywalking/oap-graalvm-native" \ + oap-graalvm-server/target/oap-graalvm-jvm-distro/oap-graalvm-jvm-distro/bin/oapService.sh + # Build distro and boot OAP with BanyanDB boot: build-distro docker-up SW_STORAGE_BANYANDB_TARGETS=localhost:17912 \ diff --git a/build-tools/precompiler/MAL-TRANSPILER-DESIGN.md b/build-tools/precompiler/MAL-TRANSPILER-DESIGN.md new file mode 100644 index 0000000..4ace716 --- /dev/null +++ b/build-tools/precompiler/MAL-TRANSPILER-DESIGN.md @@ -0,0 +1,219 @@ +# Phase 3: MAL-to-Java Transpiler Design + +## Context + +Phase 2 (Groovy pre-compilation) is complete. Phase 3 Step 1 (functional interfaces) and Step 2 (SampleFamily replacement) are done. This document covers Steps 3-6: the transpiler, Expression.java replacement, DSL/FilterExpression updates, and Precompiler wiring. + +**Goal**: Eliminate Groovy from runtime. Generate pure Java classes at build time implementing `MalExpression` and `MalFilter`. Groovy is still used at build time (for AST parsing and the MetricConvert/Javassist pipeline) but NOT at runtime. + +--- + +## Architecture + +``` +MalToJavaTranspiler +├── transpile(metricName, expression) → generates MalExpr_<name>.java +├── transpileFilter(filterLiteral) → generates MalFilter_<N>.java +├── writeManifests() → mal-expressions.txt + mal-filter-expressions.properties +├── compileAll() → javax.tools.JavaCompiler on generated .java files +│ +├── (internal) parseToAST(expression) → Groovy CompilationUnit → ModuleNode +├── (internal) visitExpression(ASTNode) → String (Java code fragment) +├── (internal) visitMethodCall(node) → method chain translation +├── (internal) visitBinary(node) → arithmetic with operand-swap logic +├── (internal) visitClosure(node, type) → Java lambda +├── (internal) visitProperty(node) → tags.get("key") or samples.get("name") +└── (internal) visitConstant(node) → literals, enums +``` + +--- + +## Generated Class Template (Expressions) + +```java +package org.apache.skywalking.oap.server.core.source.oal.rt.mal; +import java.util.*; +import org.apache.skywalking.oap.meter.analyzer.dsl.*; +import org.apache.skywalking.oap.meter.analyzer.dsl.SampleFamilyFunctions.*; +import org.apache.skywalking.oap.server.core.analysis.Layer; +import org.apache.skywalking.oap.server.core.source.DetectPoint; +import org.apache.skywalking.oap.meter.analyzer.dsl.tagOpt.K8sRetagType; + +public class MalExpr_meter_xxx implements MalExpression { + @Override + public SampleFamily run(Map<String, SampleFamily> samples) { + ExpressionParsingContext.get().ifPresent(ctx -> { + ctx.samples.add("metric_name"); + }); + SampleFamily v0 = samples.getOrDefault("metric_name", SampleFamily.EMPTY); + return v0.sum(List.of("a", "b")).service(List.of("svc"), Layer.GENERAL); + } +} +``` + +**ExpressionParsingContext tracking**: Generated `run()` adds all referenced sample names to `ExpressionParsingContext.samples`. Needed for `Expression.parse()` which calls `run(ImmutableMap.of())`. SampleFamily methods already update scope/function/labels/histogram/downsampling in the parsing context. + +**metricName on RunningContext**: `Expression.run()` sets `sf.context.setMetricName(metricName)` on all input SampleFamily objects before calling `MalExpression.run()`. + +--- + +## AST Node → Java Mapping + +### Core Nodes + +| Groovy AST | Java Output | Example | +|---|---|---| +| `VariableExpression` (sample name) | `samples.getOrDefault("name", SampleFamily.EMPTY)` | `metric_name` → local var | +| `VariableExpression` (DownsamplingType) | `DownsamplingType.X` | `SUM` → `DownsamplingType.SUM` | +| `MethodCallExpression` | Direct method call | `.sum(['a'])` → `.sum(List.of("a"))` | +| `ConstantExpression` (string) | `"string"` | `'PT1M'` → `"PT1M"` | +| `ConstantExpression` (number) | number literal | `100` → `100` | +| `ListExpression` (strings) | `List.of(...)` | `['a','b']` → `List.of("a", "b")` | +| `ListExpression` (integers) | `List.of(...)` | `[50,75,90]` → `List.of(50, 75, 90)` | +| `PropertyExpression` (enum) | Qualified enum | `Layer.GENERAL` → `Layer.GENERAL` | + +### Binary Operations + +| Groovy | Java | Notes | +|---|---|---| +| `SF + SF` | `left.plus(right)` | | +| `SF - SF` | `left.minus(right)` | | +| `SF * SF` | `left.multiply(right)` | | +| `SF / SF` | `left.div(right)` | | +| `SF + N` | `sf.plus(N)` | | +| `SF - N` | `sf.minus(N)` | | +| `SF * N` | `sf.multiply(N)` | | +| `SF / N` | `sf.div(N)` | | +| `N + SF` | `sf.plus(N)` | Swap operands | +| `N - SF` | `sf.minus(N).negative()` | From ExpandoMetaClass | +| `N * SF` | `sf.multiply(N)` | Swap operands | +| `N / SF` | `sf.newValue(v -> N / v)` | From ExpandoMetaClass | + +### Closure Patterns + +#### `.tag(Closure)` → `TagFunction` lambda + +| Groovy inside closure | Java output | +|---|---| +| `tags.key` (read) | `tags.get("key")` | +| `tags['key']` (subscript read) | `tags.get("key")` | +| `tags[expr]` (dynamic subscript) | `tags.get(expr)` | +| `tags.key = val` (write) | `tags.put("key", val)` | +| `tags[expr] = val` (dynamic write) | `tags.put(expr, val)` | +| `tags.remove('key')` | `tags.remove("key")` | +| `tags.ApiId ? A : B` | `(tags.get("ApiId") != null && !tags.get("ApiId").isEmpty()) ? A : B` | +| `tags['x']?.trim()` | `tags.get("x") != null ? tags.get("x").trim() : null` | +| `expr ?: 'default'` | `(expr != null ? expr : "default")` | +| `if (...) { ... }` | Same in Java | +| `if (...) { ... } else { ... }` | Same in Java | + +#### File-level `filter:` → `MalFilter` class + +| Groovy | Java | +|---|---| +| `tags.key == 'val'` | `"val".equals(tags.get("key"))` | +| `tags.key in ['a', 'b']` | `List.of("a", "b").contains(tags.get("key"))` | +| `tags.key` (truthiness) | `tags.get("key") != null && !tags.get("key").isEmpty()` | +| `!tags.key` (negated) | `tags.get("key") == null \|\| tags.get("key").isEmpty()` | +| `{compound && (a \|\| b)}` | Unwrap block, translate body | + +#### `.forEach(List, Closure)` → `ForEachFunction` lambda + +| Groovy | Java | +|---|---| +| `tags[prefix + '_key']` | `tags.get(prefix + "_key")` | +| `tags[prefix + '_key'] = val` | `tags.put(prefix + "_key", val)` | +| `tags.service` | `tags.get("service")` | +| `return` (early exit) | `return` (same in BiConsumer lambda) | +| `ProcessRegistry.method(...)` | Same static call | + +#### `.instance(..., Closure)` propertiesExtractor → `PropertiesExtractor` lambda + +`{tags -> ['pod': tags.pod, 'namespace': tags.namespace]}` → `tags -> Map.of("pod", tags.get("pod"), "namespace", tags.get("namespace"))` + +--- + +## Known DownsamplingType Constants + +Bare names in Groovy → qualified in Java: +- `AVG` → `DownsamplingType.AVG` +- `SUM` → `DownsamplingType.SUM` +- `LATEST` → `DownsamplingType.LATEST` +- `SUM_PER_MIN` → `DownsamplingType.SUM_PER_MIN` +- `MAX` → `DownsamplingType.MAX` +- `MIN` → `DownsamplingType.MIN` + +--- + +## Expression.java Replacement + +Same FQCN as upstream. Uses `MalExpression` instead of `DelegatingScript`. + +No `empower()`, no `ExpandoMetaClass`, no `ExpressionDelegate`, no `ThreadLocal`. + +```java +public class Expression { + private final String metricName; + private final String literal; + private final MalExpression expression; + + public Expression(String metricName, String literal, MalExpression expression) { ... } + + public ExpressionParsingContext parse() { + try (ExpressionParsingContext ctx = ExpressionParsingContext.create()) { + Result r = run(ImmutableMap.of()); + if (!r.isSuccess() && r.isThrowable()) { + throw new ExpressionParsingException(...); + } + ctx.validate(literal); + return ctx; + } + } + + public Result run(Map<String, SampleFamily> sampleFamilies) { + for (SampleFamily sf : sampleFamilies.values()) { + if (sf != null && sf != SampleFamily.EMPTY) { + sf.context.setMetricName(metricName); + } + } + try { + SampleFamily sf = expression.run(sampleFamilies); + if (sf == SampleFamily.EMPTY) { return Result.fail(...); } + return Result.success(sf); + } catch (Throwable t) { + return Result.fail(t); + } + } +} +``` + +--- + +## Precompiler Integration + +`compileMAL()` changes: +1. Still runs MetricConvert for Javassist meter class generation (unchanged) +2. Groovy scripts → temp dir (not exported) +3. Transpiler generates Java expression .class files → output dir +4. Manifests: `mal-expressions.txt` (replaces `mal-groovy-scripts.txt`), `mal-filter-expressions.properties` (replaces `mal-filter-scripts.properties`) + +**Combination pattern**: Transpiler tracks duplicate metric names, appends `_1`, `_2` suffixes. + +--- + +## Files Summary + +### New Files +| File | Purpose | +|---|---| +| `build-tools/precompiler/.../MalToJavaTranspiler.java` | AST-walking transpiler | +| `oap-libs-for-graalvm/meter-analyzer-for-graalvm/.../dsl/Expression.java` | Same-FQCN: MalExpression instead of DelegatingScript | + +### Modified Files +| File | Change | +|---|---| +| `.../meter-analyzer-for-graalvm/.../dsl/DSL.java` | Load MalExpression; manifest: mal-expressions.txt | +| `.../meter-analyzer-for-graalvm/.../dsl/FilterExpression.java` | Load MalFilter; manifest: mal-filter-expressions.properties | +| `.../meter-analyzer-for-graalvm/.../dsl/SampleFamily.java` | `newValue()` → `public` | +| `.../meter-analyzer-for-graalvm/pom.xml` | Add Expression.class to shade excludes | +| `build-tools/precompiler/.../Precompiler.java` | Add transpiler call in compileMAL() | diff --git a/oap-graalvm-native/pom.xml b/oap-graalvm-native/pom.xml index b91e86e..b666361 100644 --- a/oap-graalvm-native/pom.xml +++ b/oap-graalvm-native/pom.xml @@ -42,4 +42,41 @@ <scope>provided</scope> </dependency> </dependencies> + + <profiles> + <profile> + <id>native</id> + <build> + <plugins> + <plugin> + <groupId>org.graalvm.buildtools</groupId> + <artifactId>native-maven-plugin</artifactId> + <version>${graalvm.buildtools.version}</version> + <extensions>true</extensions> + <configuration> + <mainClass>org.apache.skywalking.oap.server.graalvm.GraalVMOAPServerStartUp</mainClass> + <imageName>oap-server</imageName> + <metadataRepository> + <enabled>true</enabled> + </metadataRepository> + <buildArgs> + <buildArg>--no-fallback</buildArg> + <buildArg>--verbose</buildArg> + <buildArg>-H:+ReportExceptionStackTraces</buildArg> + </buildArgs> + </configuration> + <executions> + <execution> + <id>build-native</id> + <goals> + <goal>compile-no-fork</goal> + </goals> + <phase>package</phase> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> </project> \ No newline at end of file diff --git a/oap-libs-for-graalvm/meter-analyzer-for-graalvm/pom.xml b/oap-libs-for-graalvm/meter-analyzer-for-graalvm/pom.xml index 2173682..162eb27 100644 --- a/oap-libs-for-graalvm/meter-analyzer-for-graalvm/pom.xml +++ b/oap-libs-for-graalvm/meter-analyzer-for-graalvm/pom.xml @@ -67,6 +67,8 @@ <exclude>org/apache/skywalking/oap/meter/analyzer/dsl/FilterExpression$*.class</exclude> <exclude>org/apache/skywalking/oap/meter/analyzer/dsl/SampleFamily.class</exclude> <exclude>org/apache/skywalking/oap/meter/analyzer/dsl/SampleFamily$*.class</exclude> + <exclude>org/apache/skywalking/oap/meter/analyzer/dsl/Expression.class</exclude> + <exclude>org/apache/skywalking/oap/meter/analyzer/dsl/Expression$*.class</exclude> <exclude>org/apache/skywalking/oap/meter/analyzer/dsl/ExpressionParsingContext.class</exclude> <exclude>org/apache/skywalking/oap/meter/analyzer/dsl/ExpressionParsingContext$*.class</exclude> <exclude>org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/InstanceEntityDescription.class</exclude> diff --git a/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/DSL.java b/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/DSL.java index d766fcf..c728c4f 100644 --- a/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/DSL.java +++ b/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/DSL.java @@ -17,7 +17,6 @@ package org.apache.skywalking.oap.meter.analyzer.dsl; -import groovy.util.DelegatingScript; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -29,17 +28,13 @@ import java.util.concurrent.atomic.AtomicInteger; import lombok.extern.slf4j.Slf4j; /** - * GraalVM replacement for upstream MAL DSL. - * Original: skywalking/oap-server/analyzer/meter-analyzer/src/main/java/.../dsl/DSL.java - * Repackaged into meter-analyzer-for-graalvm via maven-shade-plugin (replaces original .class in shaded JAR). - * - * Change: Complete rewrite. Loads pre-compiled Groovy DelegatingScript classes from - * META-INF/mal-groovy-scripts.txt manifest instead of GroovyShell runtime compilation. - * Why: Groovy runtime compilation is incompatible with GraalVM native image. + * Same-FQCN replacement for upstream MAL DSL. + * Loads transpiled MalExpression classes from mal-expressions.txt manifest + * instead of Groovy DelegatingScript classes — no Groovy runtime needed. */ @Slf4j public final class DSL { - private static final String MANIFEST_PATH = "META-INF/mal-groovy-scripts.txt"; + private static final String MANIFEST_PATH = "META-INF/mal-expressions.txt"; private static volatile Map<String, String> SCRIPT_MAP; private static final AtomicInteger LOADED_COUNT = new AtomicInteger(); @@ -54,22 +49,22 @@ public final class DSL { String className = scriptMap.get(metricName); if (className == null) { throw new IllegalStateException( - "Pre-compiled MAL script not found for metric: " + metricName - + ". Available: " + scriptMap.keySet()); + "Transpiled MAL expression not found for metric: " + metricName + + ". Available: " + scriptMap.size() + " expressions"); } try { - Class<?> scriptClass = Class.forName(className); - DelegatingScript script = (DelegatingScript) scriptClass.getDeclaredConstructor().newInstance(); + Class<?> exprClass = Class.forName(className); + MalExpression malExpr = (MalExpression) exprClass.getDeclaredConstructor().newInstance(); int count = LOADED_COUNT.incrementAndGet(); - log.debug("Loaded pre-compiled MAL script [{}/{}]: {}", count, scriptMap.size(), metricName); - return new Expression(metricName, expression, script); + log.debug("Loaded transpiled MAL expression [{}/{}]: {}", count, scriptMap.size(), metricName); + return new Expression(metricName, expression, malExpr); } catch (ClassNotFoundException e) { throw new IllegalStateException( - "Pre-compiled MAL script class not found: " + className, e); + "Transpiled MAL expression class not found: " + className, e); } catch (ReflectiveOperationException e) { throw new IllegalStateException( - "Failed to instantiate pre-compiled MAL script: " + className, e); + "Failed to instantiate transpiled MAL expression: " + className, e); } } @@ -84,7 +79,7 @@ public final class DSL { Map<String, String> map = new HashMap<>(); try (InputStream is = DSL.class.getClassLoader().getResourceAsStream(MANIFEST_PATH)) { if (is == null) { - log.warn("MAL script manifest not found: {}", MANIFEST_PATH); + log.warn("MAL expression manifest not found: {}", MANIFEST_PATH); SCRIPT_MAP = map; return map; } @@ -96,16 +91,19 @@ public final class DSL { if (line.isEmpty()) { continue; } - String[] parts = line.split("=", 2); - if (parts.length == 2) { - map.put(parts[0], parts[1]); + // mal-expressions.txt format: one FQCN per line + // Class name convention: ...mal.MalExpr_<metricName> + String simpleName = line.substring(line.lastIndexOf('.') + 1); + if (simpleName.startsWith("MalExpr_")) { + String metric = simpleName.substring("MalExpr_".length()); + map.put(metric, line); } } } } catch (IOException e) { - throw new IllegalStateException("Failed to load MAL script manifest", e); + throw new IllegalStateException("Failed to load MAL expression manifest", e); } - log.info("Loaded {} pre-compiled MAL scripts from manifest", map.size()); + log.info("Loaded {} transpiled MAL expressions from manifest", map.size()); SCRIPT_MAP = map; return map; } diff --git a/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/InstanceEntityDescription.java b/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/InstanceEntityDescription.java new file mode 100644 index 0000000..e047b67 --- /dev/null +++ b/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/EntityDescription/InstanceEntityDescription.java @@ -0,0 +1,51 @@ +/* + * 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.EntityDescription; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import org.apache.skywalking.oap.server.core.analysis.Layer; +import org.apache.skywalking.oap.server.core.analysis.meter.ScopeType; + +/** + * GraalVM replacement for upstream InstanceEntityDescription. + * Change: Closure<Map> → Function<Map, Map> for propertiesExtractor. + */ +@Getter +@RequiredArgsConstructor +@ToString +public class InstanceEntityDescription implements EntityDescription { + private final ScopeType scopeType = ScopeType.SERVICE_INSTANCE; + private final List<String> serviceKeys; + private final List<String> instanceKeys; + private final Layer layer; + private final String serviceDelimiter; + private final String instanceDelimiter; + private final Function<Map<String, String>, Map<String, String>> propertiesExtractor; + + @Override + public List<String> getLabelKeys() { + return Stream.concat(this.serviceKeys.stream(), this.instanceKeys.stream()).collect(Collectors.toList()); + } +} diff --git a/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Expression.java b/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Expression.java new file mode 100644 index 0000000..5d4fe5f --- /dev/null +++ b/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Expression.java @@ -0,0 +1,90 @@ +/* + * 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.Map; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +/** + * Same-FQCN replacement for upstream Expression. + * Wraps a transpiled {@link MalExpression} (pure Java) instead of a Groovy DelegatingScript. + * + * Upstream uses propertyMissing() delegate pattern + ExpandoMetaClass for Number ops. + * This replacement is much simpler: MalExpression.run() already has all sample lookups + * and arithmetic baked into the generated Java code. + */ +@Slf4j +@ToString(of = {"literal"}) +public class Expression { + + private final String metricName; + private final String literal; + private final MalExpression expression; + + public Expression(final String metricName, final String literal, final MalExpression expression) { + this.metricName = metricName; + this.literal = literal; + this.expression = expression; + } + + /** + * Parse the expression statically. + * + * @return Parsed context of the expression. + */ + public ExpressionParsingContext parse() { + try (ExpressionParsingContext ctx = ExpressionParsingContext.create()) { + Result r = run(ImmutableMap.of()); + if (!r.isSuccess() && r.isThrowable()) { + throw new ExpressionParsingException( + "failed to parse expression: " + literal + ", error:" + r.getError()); + } + if (log.isDebugEnabled()) { + log.debug("\"{}\" is parsed", literal); + } + ctx.validate(literal); + return ctx; + } + } + + /** + * Run the expression with a data map. + * + * @param sampleFamilies a data map includes all of candidates to be analysis. + * @return The result of execution. + */ + public Result run(final Map<String, SampleFamily> sampleFamilies) { + try { + SampleFamily sf = expression.run(sampleFamilies); + if (sf == SampleFamily.EMPTY) { + if (ExpressionParsingContext.get().isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("result of {} is empty by \"{}\"", sampleFamilies, literal); + } + } + return Result.fail("Parsed result is an EMPTY sample family"); + } + return Result.success(sf); + } catch (Throwable t) { + log.error("failed to run \"{}\"", literal, t); + return Result.fail(t); + } + } +} diff --git a/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/FilterExpression.java b/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/FilterExpression.java index 35252da..cb18a22 100644 --- a/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/FilterExpression.java +++ b/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/FilterExpression.java @@ -17,8 +17,6 @@ package org.apache.skywalking.oap.meter.analyzer.dsl; -import groovy.lang.Closure; -import groovy.lang.Script; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; @@ -30,25 +28,20 @@ import lombok.ToString; import lombok.extern.slf4j.Slf4j; /** - * GraalVM replacement for upstream FilterExpression. - * Original: skywalking/oap-server/analyzer/meter-analyzer/src/main/java/.../dsl/FilterExpression.java - * Repackaged into meter-analyzer-for-graalvm via maven-shade-plugin (replaces original .class in shaded JAR). - * - * Change: Complete rewrite. Loads pre-compiled Groovy filter closure classes from - * META-INF/mal-filter-scripts.properties manifest instead of GroovyShell runtime compilation. - * Why: Groovy runtime compilation is incompatible with GraalVM native image. + * Same-FQCN replacement for upstream FilterExpression. + * Loads transpiled {@link MalFilter} classes from mal-filter-expressions.properties + * manifest instead of Groovy filter closures — no Groovy runtime needed. */ @Slf4j @ToString(of = {"literal"}) public class FilterExpression { - private static final String MANIFEST_PATH = "META-INF/mal-filter-scripts.properties"; + private static final String MANIFEST_PATH = "META-INF/mal-filter-expressions.properties"; private static volatile Map<String, String> FILTER_MAP; private static final AtomicInteger LOADED_COUNT = new AtomicInteger(); private final String literal; - private final Closure<Boolean> filterClosure; + private final MalFilter malFilter; - @SuppressWarnings("unchecked") public FilterExpression(final String literal) { this.literal = literal; @@ -56,22 +49,21 @@ public class FilterExpression { String className = filterMap.get(literal); if (className == null) { throw new IllegalStateException( - "Pre-compiled filter script not found for: " + literal + "Transpiled MAL filter not found for: " + literal + ". Available filters: " + filterMap.size()); } try { - Class<?> scriptClass = Class.forName(className); - Script filterScript = (Script) scriptClass.getDeclaredConstructor().newInstance(); - filterClosure = (Closure<Boolean>) filterScript.run(); + Class<?> filterClass = Class.forName(className); + malFilter = (MalFilter) filterClass.getDeclaredConstructor().newInstance(); int count = LOADED_COUNT.incrementAndGet(); - log.debug("Loaded pre-compiled filter script [{}/{}]: {}", count, filterMap.size(), literal); + log.debug("Loaded transpiled MAL filter [{}/{}]: {}", count, filterMap.size(), literal); } catch (ClassNotFoundException e) { throw new IllegalStateException( - "Pre-compiled filter script class not found: " + className, e); + "Transpiled MAL filter class not found: " + className, e); } catch (ReflectiveOperationException e) { throw new IllegalStateException( - "Failed to instantiate pre-compiled filter script: " + className, e); + "Failed to instantiate transpiled MAL filter: " + className, e); } } @@ -79,7 +71,7 @@ public class FilterExpression { try { Map<String, SampleFamily> result = new HashMap<>(); for (Map.Entry<String, SampleFamily> entry : sampleFamilies.entrySet()) { - SampleFamily afterFilter = entry.getValue().filter(tags -> filterClosure.call(tags)); + SampleFamily afterFilter = entry.getValue().filter(malFilter::test); if (!Objects.equals(afterFilter, SampleFamily.EMPTY)) { result.put(entry.getKey(), afterFilter); } @@ -102,7 +94,7 @@ public class FilterExpression { Map<String, String> map = new HashMap<>(); try (InputStream is = FilterExpression.class.getClassLoader().getResourceAsStream(MANIFEST_PATH)) { if (is == null) { - log.warn("Filter script manifest not found: {}", MANIFEST_PATH); + log.warn("MAL filter manifest not found: {}", MANIFEST_PATH); FILTER_MAP = map; return map; } @@ -110,9 +102,9 @@ public class FilterExpression { props.load(is); props.forEach((k, v) -> map.put((String) k, (String) v)); } catch (IOException e) { - throw new IllegalStateException("Failed to load filter script manifest", e); + throw new IllegalStateException("Failed to load MAL filter manifest", e); } - log.info("Loaded {} pre-compiled filter scripts from manifest", map.size()); + log.info("Loaded {} transpiled MAL filters from manifest", map.size()); FILTER_MAP = map; return map; } diff --git a/pom.xml b/pom.xml index 85d9fff..f55d62d 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,7 @@ Do not hardcode — always build via `make`. --> <skywalking.version>10.4.0-SNAPSHOT</skywalking.version> <graalvm.version>25.0.0</graalvm.version> + <graalvm.buildtools.version>0.10.4</graalvm.buildtools.version> <lombok.version>1.18.40</lombok.version> <maven-checkstyle-plugin.version>3.1.0</maven-checkstyle-plugin.version> <checkstyle.version>6.18</checkstyle.version>
