Introduce MAL/LAL/Hierarchy V2 engine — replace Groovy DSL runtime with ANTLR4 + Javassist#13723
Introduce MAL/LAL/Hierarchy V2 engine — replace Groovy DSL runtime with ANTLR4 + Javassist#13723
Conversation
Document the detailed implementation plan for eliminating Groovy from OAP runtime via build-time transpilers (MAL/LAL) and v1/v2 module split (hierarchy), based on Discussion #13716 and skywalking-graalvm-distro. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add MalExpression, MalFilter, LalExpression functional interfaces and SampleFamilyFunctions (TagFunction, SampleFilter, ForEachFunction, DecorateFunction, PropertiesExtractor). Add Java functional interface overloads alongside existing Groovy Closure methods in SampleFamily, FilterSpec, ExtractorSpec, and SinkSpec. Change InstanceEntityDescription to use Function instead of Closure. All 129 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…hase 2) Ports MalToJavaTranspiler from skywalking-graalvm-distro into a new mal-transpiler analyzer submodule. The transpiler parses Groovy MAL expressions/filters via AST at CONVERSION phase and emits equivalent Java classes implementing MalExpression/MalFilter interfaces from Phase 1. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…hase 3) Introduces lal-transpiler module that parses LAL Groovy DSL scripts into AST at Phases.CONVERSION and emits pure Java classes implementing LalExpression. Handles filter/text/json/yaml/extractor/sink/abort blocks, parsed property access, safe navigation, cast expressions, GString interpolation, and SHA-256 deduplication. Makes MalToJavaTranspiler.escapeJava() public for cross-module reuse. Includes 37 comprehensive tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ing (Phase 4) Introduces meter-analyzer-v2 and log-analyzer-v2 modules that provide same-FQCN replacement classes for DSL.java, Expression.java, and FilterExpression.java. The v2 classes load transpiled MalExpression/ MalFilter/LalExpression implementations from META-INF manifests via Class.forName() instead of Groovy GroovyShell/ExpandoMetaClass/ DelegatingScript. Uses maven-shade-plugin to overlay the upstream Groovy-dependent classes. Includes 7 unit tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… (Phase 5) Extract hierarchy matching rules from HierarchyDefinitionService into pluggable HierarchyRuleProvider interface. Remove Groovy imports from server-core by replacing Closure<Boolean> with BiFunction<Service,Service,Boolean>. - hierarchy-v1: GroovyHierarchyRuleProvider (for CI checker only) - hierarchy-v2: JavaHierarchyRuleProvider with 4 built-in rules + 12 tests - HierarchyDefinitionService: add HierarchyRuleProvider interface, DefaultJavaRuleProvider - HierarchyService: .getClosure().call() → .match() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ase 6) Three checker modules verify v1 (Groovy) and v2 (transpiled Java) produce identical results: hierarchy rules (22 tests), MAL expressions (1187 tests), MAL filters (29 tests), and LAL scripts (10 tests). Zero behavioral divergences found when both paths succeed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…L to on-the-fly compilation
- Merge mal-grammar + mal-compiler into meter-analyzer
- Merge lal-grammar + lal-compiler into log-analyzer
- Merge hierarchy-rule-grammar + hierarchy-rule-compiler into hierarchy
- Remove 6 standalone modules (3 grammar + 3 compiler)
- Update DSL.java to compile MAL expressions on-the-fly via MALClassGenerator
instead of loading from non-existent manifest file
- Add varargs handling for tagEqual/tagNotEqual/tagMatch/tagNotMatch in
generated Javassist code (wrap String args in new String[]{})
- Update test/script-compiler checker POMs to reference merged module names
- Update CLAUDE.md files with merged file structure and paths
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix MAL sample collection regression: skip downsampling() method
arguments to prevent enum values (MAX, SUM, MIN) from being
collected as sample names
- Fix MAL safe navigation (?.): parser now correctly propagates
safeNav flag to chain segments; code generator uses local
StringBuilder to avoid corrupting parent buffer
- Fix MAL filter grammar: add closureCondition alternatives to
closureBody rule for bare conditions like { tags -> tags.x == 'v' }
- Fix MAL downsampling detection for bare identifiers parsed as
ExprArgument wrapping MetricExpr
- Fix MAL sample ordering: use LinkedHashSet for consistent order
- Fix LAL tag() function call: add functionName rule allowing TAG
token in functionInvocation for if(tag("LOG_KIND") == ...) patterns
- Fix LAL ProcessRegistry support: add PROCESS_REGISTRY to
valueAccessPrimary grammar rule
- Fix LAL tag statement code generation: wrap single tag entries in
Collections.singletonMap() since ExtractorSpec.tag() accepts Map
- Fix LAL makeComparison to handle CondFunctionCallContext properly
- Add debug logging to all three code generators (MAL, LAL, Hierarchy)
showing AST and generated Java source at DEBUG level
- Add generateFilterSource() to MALClassGenerator for testing
- Add error handling unit tests with demo error comments for MAL (5),
LAL (4), and Hierarchy (4) generators
- All 1248 checker tests pass: MAL 1187, Filter 29, LAL 10, Hierarchy 22
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ng all four DSL compilers (OAL, MAL, LAL, Hierarchy). Remove Groovy references from docs: LAL code blocks, hierarchy matching rule labels, and stale MeterProcessor comment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Provides a /run-e2e slash command with prerequisites (e2e CLI, swctl, yq install instructions), rebuild detection, test execution, and failure debugging workflow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, interpolated sampler IDs
Address five critical gaps in the LAL v2 compiler that broke shipped production rules:
1. tag("LOG_KIND") in conditions now emits tagValue() helper instead of null
2. Safe navigation (?.) for method calls emits safeCall() helper to prevent NPE
3. Metrics, slowSql, sampledTrace, sampler/rateLimit blocks generate proper
sub-consumer classes with BindingAware wiring
4. else-if chains build nested IfBlock AST nodes instead of dropping
intermediate branches
5. GString interpolation in rateLimit IDs (e.g. "${log.service}:${parsed.code}")
parsed into InterpolationPart segments and emitted as string concatenation
Also fixes ProcessRegistry static calls to pass arguments through, and adds
comprehensive tests (55 total: 35 generator + 20 parser) covering all gaps
including production-like envoy-als, nginx, and k8s-service rule patterns.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cripts and runtime comparison - Rename test/script-compiler to test/script-cases/script-runtime-with-groovy - Copy all shipped production configs into test/script-cases/scripts/ as test copies (MAL: test-otel-rules, test-meter-analyzer-config, test-log-mal-rules, test-envoy-metrics-rules; LAL: test-lal; Hierarchy: test-hierarchy-definition.yml) - Update all checker tests to load from shared scripts/ directory - Upgrade LAL checker from compile-only to full runtime execution comparison (v1 Groovy vs v2 ANTLR4+Javassist, comparing Binding state: service, layer, tags, abort/save) - Update Maven coordinates and root pom module path Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move benchmarks from the standalone oap-server/microbench module into the src/test/ directories of the modules they actually test (server-core and library-util). Drop AbstractMicrobenchmark base class in favor of self-contained @test run() methods. Bump JMH 1.21 -> 1.37 and remove the obsolete -XX:BiasedLockingStartupDelay=0 JVM flag (removed in JDK 18). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MAL compiler fixes (closes 38 previously failing expressions): - Add ternary operator (?:) support in closures (grammar, AST, codegen) - Fix valueEqual() and other primitive-double methods with numeric literal args - Support double-paren argument syntax: sum((['cluster'])) - Handle NUMBER / SampleFamily via MalRuntimeHelper.divReverse() in v2 package - Add variable declarations, map literals, forEach/instance closure types - Add ProcessRegistry class references, improved safe navigation LAL compiler fixes: - Fix null-to-string conversion: use null-safe toStr() instead of String.valueOf() - Add camelToSnake field name fallback for protobuf field access - Add typed execute(FilterSpec, Binding) method signature - Reorganize LAL test scripts into oap-cases/ and feature-cases/ - Add data-driven LALExpressionExecutionTest with 27 test cases MAL checker enhancements: - Add runtime execution comparison (mock SampleFamily data, execute both v1 and v2, compare output samples with labels and values) - Handle increase()/rate() by priming CounterWindow with initial run - Extract tagEqual patterns from expressions for matching mock data All 1,187 MAL + 29 LAL + 22 hierarchy expressions now pass with zero gaps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…L typed signature Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Give v2 (ANTLR4+Javassist) classes distinct FQCNs from v1 (Groovy) so both can coexist on the classpath without source duplication in v1-with-groovy test modules. Package mapping: - MAL: meter.analyzer.* → meter.analyzer.v2.* - LAL: log.analyzer.* → log.analyzer.v2.* - Hierarchy: config.compiler.* → config.v2.compiler.* Also: remove v2-only files (MalExpression, MalFilter, LalExpression) from v1-with-groovy modules, add mal-v1-with-groovy dependency to lal-v1-with-groovy, fix cross-version enum comparison by name. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ime() scalar in MAL compiler Add ANTLR4 lexer mode for regex literals (=~ /pattern/), def keyword with type inference from initializer (String[][] for regex, String[] for split), GString interpolation expansion, .size() to .length translation, decorate() bean-mode closures, and time() as a scalar function in binary expressions. Verified with 1,228 v1-v2 checker tests (1,197 MAL + 31 filter). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…v1-v2 checker data Rewrite MAL run() code generation to use a single reassigned 'sf' variable instead of multiple intermediate variables, producing cleaner decompiled output. Add LocalVariableTable attribute so decompilers show 'samples' and 'sf' instead of 'var1' and 'var2'. Integrate v2 compilers with runtime wiring, add checker test data files, and clean up unused code across MAL/LAL/Hierarchy modules. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add LVT attribute to LAL execute() and consumer accept() methods, and to Hierarchy apply() method, so decompilers show meaningful variable names (filterSpec, binding, _t, u, l) instead of var0, var1, etc. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move 8 helper methods (getAt, toLong, toInt, toStr, toBool, isTruthy, tagValue, safeCall) from being duplicated in every generated class via addHelperMethods() to a shared LalRuntimeHelper in the rt package. Generated code now calls LalRuntimeHelper.toStr() etc. via FQCN. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…th typed methods Fix rateLimit() calls inside if-blocks within sampler generating empty bytecode by handling the samplerContent grammar alternative in LALScriptParser.visitIfBody(). Replace generic LalRuntimeHelper.safeCall() and isTruthy() with specific typed methods: isTrue() for Boolean conditions, isNotEmpty() for String non-emptiness, toString() and trim() for null-safe navigation — making generated code explicit about intended type semantics. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…licitly, add LVT - Merge consumer sub-classes into single generated class with private methods - Remove BINDING ThreadLocal from AbstractSpec; all spec methods take ExecutionContext explicitly - Delete BindingAware.java and Binding.java, replace with ExecutionContext - Add abort guard before _extractor/_sink calls matching v1 Groovy behavior - Add LocalVariableTable to all generated methods (execute, _extractor, _sink) - Rename binding→ctx throughout for consistency - Add extraLogType to envoy-als.yaml for compile-time proto resolution - Remove all Consumer callback methods from spec files - Add finalizeSink abort check in FilterSpec Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…es, filters) Previously only run() had LVT. Now all generated methods have named locals in debuggers/decompilers instead of var0/var1/var2: - metadata(): this, _samples, _scopeLabels, _aggLabels, _pct - tag/instance apply(Map): this, param name - tag/instance apply(Object) bridge: this, o - forEach accept(): this, element, tags - decorate accept(): this, _arg, param name - filter test(): this, param name Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…version 10.4.0-SNAPSHOT
Replace CI-friendly ${revision} with hardcoded 10.4.0-SNAPSHOT in all 104 POMs.
This eliminates persistent "Could not find artifact ...pom:${revision}" errors
when building individual modules without -am. Also removes flatten-maven-plugin
(no longer needed), updates release scripts to use versions:set, and wires
LALSourceTypeProvider SPI for envoy-als extraLog type resolution in tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…chains Cache the extraLog cast in a _p local variable and break safe-nav chains into sequential _tN locals instead of deeply nested ternaries. Repeated access to the same chain prefix reuses existing variables (dedup). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove bind()/evaluate() two-phase pattern from DSL. The mutable ExecutionContext field made DSL unsafe for concurrent use. Now evaluate(ExecutionContext) takes ctx as a parameter, matching the stateless pattern already used by MAL and Hierarchy v2 runtimes. Update LogFilterListener to store per-request contexts in a list and pass each to the corresponding DSL.evaluate(ctx) call. Update LogTestQuery to use the new single-call API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MAL execute ~4.9x, LAL compile ~39x / execute ~2.8x, Hierarchy execute ~2.6x faster than Groovy v1. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ile skill - Extract MAL codegen utility methods into MALCodegenHelper - Add flatten:flatten to checkstyle command in compile skill - Add contributing guide doc for Claude Code skills - Add generate-classes skill Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ve failures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…-plugin is restored Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…or new test modules Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…eactor modules Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…to ~6.8x Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 1: Fix MalInputDataGenerator to track per-rule tagEqual/tagMatch variants, generating multiple sample variants per metric for complete coverage. Handles tagNotEqual/tagNotMatch labels in input samples. Phase 2: Add MalExpectedDataGenerator that runs v1 (Groovy) MAL expressions and captures output (entities, samples, values) as rich expected sections in .data.yaml files. Uses Mockito mockStatic for K8s metadata mocking. Phase 3: Enhance MalComparisonTest with hard assertions on expected entities (scope/service/instance/endpoint/layer) and samples (labels/ values). EMPTY is a hard failure when rich expected exists. Duplicate rule names disambiguated with _2/_3 suffix. v1 runtime errors fail instead of silently skipping. Phase 4: Add expected validation to LalComparisonTest for save, abort, service, instance, endpoint, layer, tags, timestamp, and sampledTrace fields. Fix enum comparison for reason/detectPoint fields. All 1301 tests pass (1233 MAL + 35 LAL + 1 generator + 32 hierarchy). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PowerMock is a dead project. The only usage was powermock-reflect's Whitebox class for setInternalState/getInternalState/invokeMethod — a thin wrapper around java.lang.reflect. Replace with a project-owned ReflectUtil in the server-testing module and add server-testing as test dependency to all 17 modules that previously relied on powermock-reflect. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…put data mock principles - Copy vm.yaml (telegraf) and agent.yaml (zabbix) to test script directories - Handle both 'metricsRules' and 'metrics' YAML keys (zabbix uses 'metrics') - Handle numeric YAML keys (zabbix labels like '1', '2') via String.valueOf() - Generate .data.yaml with proper label variants (e.g., cpu-total + cpu0 for tagEqual/tagNotEqual) - Add CLAUDE.md documentation for input data mock principles in MAL, LAL, hierarchy modules - Create CLAUDE.md for the checker test module Total: 1268 MAL + 35 LAL + 31 filter = 1336 tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… LAL rule Cover all branches in the k8s-service.yaml network-profiling-slow-trace rule: - Virtual local process (process_id empty, local=true) - Virtual remote process (process_id empty, local=false) - HTTP without SSL (componentId 49) - TCP with SSL (componentId 130) - Default component (componentId 110) - LOG_KIND false path (no sampledTrace extraction) Also align v1/v2 ProcessRegistry mock return values so v1-v2 comparison works correctly for virtual process branches. LAL tests: 35 → 39 (+4 new entries). Total: 1340 tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ssRegistry dependencies - Change inputData type from Map<String, Map> to Map<String, Object> to prevent ClassCastException when YAML values are Lists (multi-entry input data) - Add instanceof List handling for multi-entry input data per rule - Add @BeforeAll/@afterall mockStatic for K8sInfoRegistry and MetricsStreamProcessor (required by production ProcessRegistry for virtual process ID generation) - Remove sampledTrace.processId/destProcessId from virtual process entries in envoy-als.input.data and k8s-service.input.data — values depend on ProcessRegistry implementation (mock vs production), validated via v1-v2 comparison in checker test Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…data.yaml files Add extractEntityFunctionLabels() to parse service/instance/endpoint/process function arguments like instance(['host_name'], ['service_instance_id'], Layer.MYSQL) and ensure these labels appear in all input samples. Without entity labels, scope/service/instance extraction produces incorrect results. Regenerated .data.yaml files for rules with entity function labels. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… PowerMock changelog getInternalState() returns generic <T>, and when passed directly to setInternalState(), Java resolves the overload to setInternalState(Class<?>, ...) instead of setInternalState(Object, ...), causing ClassCastException. Fix by storing the result in a local Object variable first. Update changelog to reflect full PowerMock removal from all modules. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
# Conflicts: # docs/en/changes/changes.md
…th, regenerate expected data Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Gemini Code Review — V2 Engine Migration (Groovy Replacement) I have completed a comprehensive review of the changes in the Key Technical Highlights:
The migration successfully modernizes the DSL engine while maintaining strict compatibility with existing rule configurations. |
…oc for key code paths - Remove initExp from MAL docs (mal.md, backend-meter.md, backend-zabbix.md); it was an internal Groovy startup validation mechanism, not an end-user feature - Replace "Use Groovy script" in hierarchy-definition.yml with accurate grammar description - Add Javadoc to HierarchyRuleClassGenerator (supported grammar, SourceFile/LineNumberTable debugging, toGetter field mapping and fallback, formatSourceFileName logic) - Add Javadoc to HierarchyDefinitionService (loadProvider SPI discovery, match() behavior) - Add changelog entries for initExp removal and hierarchy rule grammar change - Add .claude/settings.local.json to .gitignore Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Gemini Verification Report: LAL/MAL/Hierarchy V2 Transition1. Architecture & Engine ParityThe migration from the Groovy-based V1 engine to the ANTLR4 + Javassist-based V2 engine was reviewed for both architectural integrity and functional parity.
2. Verification ScopeA systematic, file-by-file review was performed on all 87 script files located in
3. Branch & Feature Coverage (100% Verified)Every script was verified against its corresponding mock data (
4. Resolution of Previously Identified GapsAll gaps identified during earlier stages of the review have been successfully addressed:
ConclusionThe V2 transition is implementation-complete and thoroughly verified. The test suite provides exhaustive evidence of behavioral equivalence between the old and new engines while maintaining 100% parity with the production script set. The PR is verified as stable and high-quality. |
The env var controls class file dumping for all 4 DSL compilers (OAL, MAL, LAL, Hierarchy), not just OAL. Rename to reflect its actual scope. Update all documentation references accordingly.
Update the BanyanDB image tag in docker/.env and docker/docker-compose.yml to match the version pinned in test/e2e-v2/script/env.
Introduce MAL/LAL/Hierarchy V2 engine — replace Groovy DSL runtime with ANTLR4 + Javassist
This is a non-trivial feature. Design doc:
docs/en/academy/dsl-compiler-design.mdDocumentation updated to include this new feature.
Tests (UT, IT, E2E) are added to verify the new feature.
If this pull request closes/resolves/fixes an existing issue, replace the issue number. Closes #.
Update the
CHANGESlog.What this PR does
Introduces the MAL/LAL/Hierarchy V2 engine — replacing the Groovy-based DSL runtime for MAL (Meter Analysis Language), LAL (Log Analysis Language), and Hierarchy matching rules with compile-time ANTLR4 parsing and Javassist bytecode generation — the same approach already used by the OAL V2 engine.
All three DSL compilers follow the same pipeline:
Why
LocalVariableTable(LVT) entries with named variablesArchitecture
MalExpressionSampleFamily run(Map<String, SampleFamily>)LalExpressionvoid execute(FilterSpec, ExecutionContext)BiFunction<Service, Service, Boolean>Boolean apply(Service, Service)All v2 classes live under
.v2.packages to avoid FQCN conflicts with v1 (Groovy) classes, which remain intest/script-cases/script-runtime-with-groovy/for comparison testing.File change summary
587 files changed, +64,705 / -1,890 (bulk from new test data files and POM version changes).
.data.yaml+ 10.input.datacompanion filestest/script-cases/script-runtime-with-groovy/HierarchyDefinitionService,HierarchyService, benchmarkscompile,test,license,gh-pull-request,ci-e2e-debug,run-e2e,generate-classesGRPCServer/HTTPServerjavadoc,ReflectUtil, benchmarksSW_OAL_ENGINE_DEBUG→SW_DYNAMIC_CLASS_ENGINE_DEBUG.github/workflows/skywalking.yaml*.generated-classes/patternNew Maven modules
hierarchyoap-server/analyzer/hierarchy/script-runtime-with-groovy(parent)test/script-cases/script-runtime-with-groovy/mal-v1-with-groovy.../script-runtime-with-groovy/mal-v1-with-groovy/lal-v1-with-groovy.../script-runtime-with-groovy/lal-v1-with-groovy/mal-lal-v1-v2-checker.../script-runtime-with-groovy/mal-lal-v1-v2-checker/hierarchy-v1-v2-checker.../script-runtime-with-groovy/hierarchy-v1-v2-checker/hierarchy-v1-with-groovy.../script-runtime-with-groovy/hierarchy-v1-with-groovy/ANTLR4 grammar files (new)
oap-server/analyzer/meter-analyzer/src/main/antlr4/.../MALLexer.g4oap-server/analyzer/meter-analyzer/src/main/antlr4/.../MALParser.g4oap-server/analyzer/log-analyzer/src/main/antlr4/.../LALLexer.g4oap-server/analyzer/log-analyzer/src/main/antlr4/.../LALParser.g4oap-server/analyzer/hierarchy/src/main/antlr4/.../HierarchyRuleLexer.g4oap-server/analyzer/hierarchy/src/main/antlr4/.../HierarchyRuleParser.g4Key source files (v2 compiler)
MAL compiler (
oap-server/analyzer/meter-analyzer/src/main/java/.../v2/)compiler/MALScriptParser.javaMALExpressionModelASTcompiler/MALExpressionModel.javacompiler/MALClassGenerator.javarun()method codegen, metadata extractioncompiler/MALClosureCodegen.javacompiler/MALCodegenHelper.javacompiler/rt/MalRuntimeHelper.javadsl/SampleFamily.javav2/Analyzer.javav2/MetricConvert.javaLAL compiler (
oap-server/analyzer/log-analyzer/src/main/java/.../v2/)compiler/LALScriptParser.javaLALScriptModelASTcompiler/LALScriptModel.javacompiler/LALClassGenerator.javaexecute()method, class scaffoldingcompiler/LALBlockCodegen.javacompiler/LALCodegenHelper.javacompiler/rt/LalRuntimeHelper.javadsl/ExecutionContext.javadsl/spec/filter/FilterSpec.javadsl/spec/extractor/ExtractorSpec.javaspi/LALSourceTypeProvider.javaextraLogTypeproto class at compile timeHierarchy compiler (
oap-server/analyzer/hierarchy/src/main/java/.../v2/compiler/)HierarchyRuleScriptParser.javaHierarchyRuleModelASTHierarchyRuleModel.javaHierarchyRuleClassGenerator.javaBiFunction<Service, Service, Boolean>CompiledHierarchyRuleProvider.javaHierarchyRuleProviderGenerated .class file output
During compilation, generated
.classfiles can be dumped for debugging.Environment variable:
SW_DYNAMIC_CLASS_ENGINE_DEBUG— set to any non-empty value to enable.Output directories (per DSL, relative to working directory):
oal-rt-generated-classes/mal-generated-classes/lal-generated-classes/hierarchy-generated-classes/During checker tests, generated classes are dumped to:
These directories are git-ignored via
.gitignore.See
docs/en/operation/dynamic-code-generation-debugging.mdfor full details.v1-v2 cross-version checker tests
Located in
test/script-cases/script-runtime-with-groovy/.Test classes
MalComparisonTest.../checker/mal/MalComparisonTest.javaMalFilterComparisonTest.../checker/mal/MalFilterComparisonTest.javaMalInputDataGeneratorTest.../checker/mal/MalInputDataGeneratorTest.java.data.yamlcompanion filesMalExpectedDataGeneratorTest.../checker/mal/MalExpectedDataGeneratorTest.javaLalComparisonTest.../checker/lal/LalComparisonTest.javaHierarchyRuleComparisonTest.../core/config/HierarchyRuleComparisonTest.javaVerification counts
MalFilterComparisonTesttest-lal/directorytest-hierarchy-definition.data.yamlTest data locations
test/script-cases/scripts/mal/test-*/.data.yaml(SampleFamily mock input)test/script-cases/scripts/lal/test-lal/.input.data(proto-json LogData/extraLog)Unit tests (in v2 compiler modules)
MALScriptParserTest.../meter-analyzer/.../MALScriptParserTest.javaMALClassGeneratorTest.../meter-analyzer/.../MALClassGeneratorTest.javaDSLV2Test(MAL).../meter-analyzer/.../DSLV2Test.javaLALScriptParserTest.../log-analyzer/.../LALScriptParserTest.javaLALClassGeneratorTest.../log-analyzer/.../LALClassGeneratorTest.javaLALExpressionExecutionTest.../log-analyzer/.../LALExpressionExecutionTest.javaDSLV2Test(LAL).../log-analyzer/.../DSLV2Test.javaHierarchyRuleScriptParserTest.../hierarchy/.../HierarchyRuleScriptParserTest.javaHierarchyRuleClassGeneratorTest.../hierarchy/.../HierarchyRuleClassGeneratorTest.javaJMH benchmarks
MalBenchmark.../mal-lal-v1-v2-checker/.../MalBenchmark.javaLalBenchmark.../mal-lal-v1-v2-checker/.../LalBenchmark.javaHierarchyBenchmark.../hierarchy-v1-v2-checker/.../HierarchyBenchmark.javaResults:
Compiled code examples
MAL Example — Tag closure (inlined as method on main class)
DSL:
metric.tag({tags -> tags.service_name = 'APISIX::' + tags.skywalking_service})Generated class (single
.classfile, no separate closure class):Closures are compiled as methods on the main class. At class-load time,
LambdaMetafactorywraps each method into a functional interface instance — the same mechanismjavacuses for lambda expressions.MAL Example — Regex match with ternary
DSL:
metric.tag({ tags -> def matcher = (tags.metrics_name =~ /\.ssl\.certificate\.([^.]+)\.expiration/); tags.secret_name = matcher ? matcher[0][1] : "unknown" })Generated
_tag_apply()method:deftype inferred asString[][]from=~regex match. Ternary compiles to Java ternary with null-check onObjectcast.LAL Example — JSON parser with extractor
DSL:
filter { json {} extractor { service parsed.service as String instance parsed.instance as String } sink {} }Generated class:
Single class, no closures.
h.mapVal()accesses JSON parsed map.h.toStr()preservesnull(unlikeString.valueOf()which returns"null").LAL Example — Proto-based with extraLogType (Envoy ALS)
DSL:
filter { if (parsed?.response?.responseCode?.value as Integer < 400) { abort {} } extractor { if (parsed?.response?.responseCode) { tag 'status.code': parsed?.response?.responseCode?.value } tag 'response.flag': parsed?.commonProperties?.responseFlags } sink {} }Generated class (with
extraLogType = HTTPAccessLogEntry):Proto getter chains resolved via Java reflection at compile time — at runtime it's direct method calls.
?.safe navigation emits== null ? null :ternaries. Intermediate values cached in_tNlocal variables for readability and dedup.Hierarchy Example — Simple name match
DSL:
{ (u, l) -> u.name == l.name }Generated class:
Hierarchy Example — Block body with if/return
DSL:
{ (u, l) -> { if (l.shortName.lastIndexOf('.') > 0) { return u.shortName == l.shortName.substring(0, l.shortName.lastIndexOf('.')); } return false; } }Generated class:
Property access → getter methods.
==→Objects.equals(). Numeric>→ direct operator.SPI registration files
oap-server/analyzer/hierarchy/.../META-INF/services/...HierarchyRuleProviderCompiledHierarchyRuleProvideroap-server/analyzer/log-analyzer/.../META-INF/services/...ModuleDefineLogAnalyzerModuleoap-server/analyzer/log-analyzer/.../META-INF/services/...ModuleProviderLogAnalyzerModuleProvideroap-server/analyzer/log-analyzer/.../test/.../META-INF/services/...LALSourceTypeProvideroap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/.../META-INF/services/...LALSourceTypeProviderDocumentation
docs/en/academy/dsl-compiler-design.mddocs/en/operation/dynamic-code-generation-debugging.md.classfiles, output dirs, env vardocs/en/changes/changes.mddocs/en/setup/backend/configuration-vocabulary.mdSW_DYNAMIC_CLASS_ENGINE_DEBUGenv vardocs/en/concepts-and-designs/lal.mddocs/en/concepts-and-designs/service-hierarchy.mddocs/en/concepts-and-designs/service-hierarchy-configuration.mddocs/en/setup/backend/backend-meter.mdinitExpfrom configdocs/menu.ymlOther notable changes included in this branch
SW_OAL_ENGINE_DEBUG→SW_DYNAMIC_CLASS_ENGINE_DEBUG— unified env var for all 4 DSL compilersinitExpfrom MAL config — was internal Groovy startup validation, v2 compiler validates at startup nativelyReflectUtil(standard Java reflection +sun.misc.Unsafefor final fields)-javaagentfor Mockito/Byte Buddy in Surefire for JDK 25+ compatibilityoap-server/microbench/to target modules for better colocation