Fix custom layers: MAL v2 Layer.NAME compilation + layer-extensions.yml jar shadowing#13918
Merged
Merged
Conversation
…ns.yml jar shadowing Two independent bugs prevented custom layers (apache#13856 / apache#13883) from working end to end. 1. The v2 MAL compiler could not compile an expression that references a custom layer via Layer.nameOf('NAME') — the documented form for layers that have no generated Layer.* static field. The grammar's enumRef models only the static-field form (Layer.GENERAL); Layer.nameOf('X') was mis-parsed as a metric named "Layer" with a .nameOf("X") method chain and failed code generation with "nameOf(java.lang.String) not found in SampleFamily". The parser now reinterprets the EnumType.method('arg') shape, keyed on the known enum-type set so real metric.method('x') calls are unaffected, and emits a static call on the enum FQCN. The v1 Groovy compiler already evaluated this natively. 2. layer-extensions.yml was bundled in skywalking-oap.jar and not shipped to the distribution config/ directory. Because the launch script puts oap-libs/*.jar ahead of config/ on the classpath, ResourceUtils.read() always resolved the empty jar-bundled template (layers: []) and an operator's config/layer-extensions.yml was silently shadowed, so custom layers declared there never registered. The file now follows the same exclude-from-jar + copy-to-config/ packaging as every other operator-editable config (application.yml, alarm-settings.yml, ...). Closes apache#13917 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
e25d11a to
944d6c6
Compare
A custom layer (layerDefinitions: / layer-extensions.yml / LayerExtension SPI)
has no generated Layer.* static field, so referencing it as Layer.IOT_FLEET
failed v2 MAL code generation. The compiler now lowers every Layer.NAME
static-field reference to a runtime Layer.nameOf("NAME") registry lookup,
scoped to Layer only (the other ENUM_FQCN types are real enums and keep their
direct field access). For a built-in layer this is equivalent, since
Layer.nameOf("GENERAL") returns the same instance as Layer.GENERAL, so a custom
layer is referenced exactly like a built-in one.
This replaces the earlier Layer.nameOf('NAME') parser-reinterpretation approach
(EnumStaticCallArgument): neither form had shipped, and the field form needs no
grammar/AST/parser special-casing. Reverts that path in MALExpressionModel /
MALScriptParser and updates docs to the field form.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
wu-sheng
approved these changes
Jun 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix two independent bugs that break custom layers end-to-end. Closes #13917.
Custom layers (declared without modifying OAP source — #13856 / #13883) were broken in two independent ways. Either one alone prevents a custom layer from working.
Bug 1 — MAL v2 compiler cannot compile a custom-layer reference
A custom layer has no generated
Layer.*static field — it lives only in the name registry — so referencing it in a MAL expression failed v2 code generation. The natural formservice(['svc'], Layer.IOT_FLEET)parses as the static-fieldenumRef(IDENTIFIER DOT IDENTIFIER, exactly likeLayer.GENERAL), and codegen emitted it verbatim as aLayer.IOT_FLEETfield access — a field that does not exist onLayer— so Javassist failed to compile the generated source.Fix:
MALMethodChainCodegennow lowers everyLayer.NAMEstatic-field reference to a runtimeLayer.nameOf("NAME")registry lookup, scoped toLayeronly (viaMALCodegenHelper.LAYER_ENUM_TYPE).Layeris a registry-backed type, not a Java enum, andLayer.nameOfresolves both built-in and custom layers by name; for a built-in this is behavior-preserving, becauseLayer.nameOf("GENERAL")returns the same instance as theLayer.GENERALfield. The otherENUM_FQCNtypes (DetectPoint,DownsamplingType,TimeUnit,K8sRetagType) are real Java enums with nonameOf(String)and keep their direct static-field reference. A custom layer is therefore referenced exactly like a built-in one, with no grammar / AST / parser change.docs/.../mal.mdis updated to theLayer.NAMEform.Tests (
MALClassGeneratorTest): a custom-layer field form lowers toLayer.nameOf("...")and compiles; a built-in field form lowers identically; a non-Layerenum (DetectPoint) keeps its direct field access. All 1350 bundled-rule execution tests pass — every built-inLayer.*reference now routes throughnameOfand resolves to the same layer (no rule used a custom layer before, which is why CI never caught it).Bug 2 —
layer-extensions.ymlbundled in the jar shadows the operator'sconfig/copylayer-extensions.yml(operator-managed, documented as living inconfig/) was packaged insideskywalking-oap.jarand not shipped to the distributionconfig/. The launch script (dist-material/bin/oapService.sh:38-41) prepends everyoap-libs/*.jartoCLASSPATH, leavingconfig/last, soResourceUtils.read("layer-extensions.yml")→getClassLoader().getResource(...)deterministically resolved the empty jar-bundled template (layers: []) and silently ignored the operator's file — custom layers declared there never registered.Fix: add
layer-extensions.ymlto themaven-jar-plugin<excludes>(oap-server/server-starter/pom.xml) and to the assembly<includes>(apm-dist/src/main/assembly/binary.xml), so it follows the same exclude-from-jar + copy-to-config/packaging as every other operator-editable config. Verified by rebuildingskywalking-oap.jarand confirming the file is no longer present.CHANGESlog.