wu-sheng commented on code in PR #13737:
URL: https://github.com/apache/skywalking/pull/13737#discussion_r2912409621
##########
docs/en/concepts-and-designs/lal.md:
##########
@@ -145,6 +145,89 @@ filter {
Extractors aim to extract metadata from the logs. The metadata can be a
service name, a service instance name, an
endpoint name, or even a trace ID, all of which can be associated with the
existing traces and metrics.
+#### Local variables (`def`)
+
+You can use `def` to declare local variables in the extractor (or at the
filter level). This is useful when an
+expression is reused multiple times, or when you want to break a long chain
into readable steps.
+
+The syntax is:
+
+```
+def variableName = expression
+def variableName = expression as TypeName
+```
+
+The variable type is inferred from the initializer expression at compile time.
`def` is not limited to JSON — it works
+with any value access expression whose type is resolvable on the classpath,
including protobuf getter chains,
+`log.*` fields, and Gson JSON method chains. Subsequent method calls on the
variable are validated at compile time
+against the inferred type.
+
+You can optionally add an explicit `as` type cast to narrow the variable type.
The cast type can be a built-in
+type (`String`, `Long`, `Integer`, `Boolean`) or a fully qualified class name:
+
+```
+def value = someExpression as com.example.MyType
+```
+
+This is useful when the compiler infers a type that is too general (e.g.,
`Object` from a generic API return)
+and you know the concrete runtime type. The cast tells the compiler which type
to use for subsequent method chain
+validation. Note that `as` performs a Java cast — it does **not** convert
between types. For JSON conversion, use
+`toJson()` or `toJsonArray()` instead.
+
+The FQCN must be resolvable on the classpath at compile time. If the class is
not found, the OAP server will fail
+to start.
+
+Two built-in conversion functions are provided for JSON interoperability:
+
+- `toJson(expr)` — converts a value to a Gson `JsonObject`. Works with JSON
strings, `Map`, and protobuf `Struct`.
+- `toJsonArray(expr)` — converts a value to a Gson `JsonArray`. Works with
JSON array strings.
+
Review Comment:
Fixed in d990305. Added Map/List → Gson recursive conversion in
toJsonObject(Object) and toJsonArray(Object). Docs are now accurate.
##########
oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/v2/compiler/rt/LalRuntimeHelper.java:
##########
@@ -180,6 +188,115 @@ public String tagValue(final String key) {
return "";
}
+ // ==================== JSON conversion (for def variables)
====================
+ //
+ // toJsonObject: converts to JsonObject. Overloaded for Struct, String,
Object.
+ // toJsonArray: converts to JsonArray. Overloaded for String, Object.
+ //
+ // Primary use case: extract JWT claims from envoy ALS filter_metadata:
+ // def jwt = toJson(parsed?.commonProperties?.metadata
+ // ?.filterMetadataMap?.get("envoy.filters.http.jwt_authn"))
+ // tag 'email':
jwt?.getAsJsonObject("payload")?.get("email")?.getAsString()
+
+ /**
+ * Converts a protobuf {@link Struct} to a Gson {@link JsonObject}.
+ * Recursively converts nested Struct/Value/ListValue to Gson types.
+ */
+ public JsonObject toJsonObject(final Struct struct) {
+ if (struct == null) {
+ return null;
+ }
+ return structToJsonObject(struct);
+ }
+
+ /**
+ * Parses a JSON string into a {@link JsonObject}.
+ */
+ public JsonObject toJsonObject(final String s) {
+ if (s == null || s.isEmpty()) {
+ return null;
+ }
+ final JsonElement el = JsonParser.parseString(s);
+ return el.isJsonObject() ? el.getAsJsonObject() : null;
+ }
+
+ /**
+ * Fallback for {@code Map.get()} erasure — dispatches to typed overloads.
+ */
+ public JsonObject toJsonObject(final Object obj) {
+ if (obj == null) {
+ return null;
+ }
+ if (obj instanceof Struct) {
+ return toJsonObject((Struct) obj);
+ }
+ if (obj instanceof String) {
+ return toJsonObject((String) obj);
+ }
+ return null;
+ }
Review Comment:
Fixed in d990305. toJsonObject(Object) now dispatches Map via a new
toJsonObject(Map) overload with recursive conversion. toJsonArray(Object) also
handles List input.
##########
oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/v2/compiler/LALBlockCodegen.java:
##########
@@ -1140,6 +1172,283 @@ static String generateExtraLogAccess(
return prevVar;
}
+ // ==================== Def statement codegen ====================
+
+ static void generateDefStatement(final StringBuilder sb,
+ final LALScriptModel.DefStatement def,
+ final LALClassGenerator.GenCtx genCtx) {
+ final LALScriptModel.ValueAccess init = def.getInitializer();
+ final String varName = def.getVarName();
+ final String javaVar = "_d" + genCtx.localVarCounter++;
+
+ // Determine type and generate initializer expression
+ Class<?> resolvedType;
+ final StringBuilder initExpr = new StringBuilder();
+
+ if (init.getFunctionCallName() != null
+ && BUILTIN_FUNCTIONS.containsKey(init.getFunctionCallName())) {
+ // Built-in function: toJson(...), toJsonArray(...)
+ final Object[] info =
BUILTIN_FUNCTIONS.get(init.getFunctionCallName());
+ final String helperMethod = (String) info[0];
+ resolvedType = (Class<?>) info[1];
+
+ initExpr.append(helperMethod).append("(");
+ if (!init.getFunctionCallArgs().isEmpty()) {
+ generateValueAccess(initExpr,
+ init.getFunctionCallArgs().get(0).getValue(), genCtx);
+ } else {
+ initExpr.append("null");
+ }
+ initExpr.append(")");
+ } else {
Review Comment:
Fixed in d990305. toJson()/toJsonArray() now validate exactly 1 argument at
compile time and throw IllegalArgumentException with a clear message otherwise.
##########
oap-server/analyzer/log-analyzer/CLAUDE.md:
##########
@@ -105,6 +110,68 @@ All generated methods include a `LocalVariableTable`
attribute for debugger/deco
LVT entries are added via `PrivateMethod` inner class which carries both
source code and variable descriptors.
+## Local Variables (`def`)
+
+The `def` keyword declares local variables in the extractor (or filter level).
The grammar rule:
+
+```
+defStatement: DEF IDENTIFIER ASSIGN valueAccess typeCast? ;
+```
+
+The optional `typeCast` supports built-in types (`String`, `Long`, `Integer`,
`Boolean`) and
+fully qualified class names (`as com.example.MyType`). The FQCN is resolved
via `Class.forName()`
+at compile time. If not found, compilation fails with
`IllegalArgumentException`.
+
+### Type inference
+
+The variable type is inferred from the initializer expression:
+
+| Initializer | Inferred type | Generated code |
+|---|---|---|
+| `toJson(expr)` | `JsonObject` | `h.toJsonObject(expr)` |
+| `toJsonArray(expr)` | `JsonArray` | `h.toJsonArray(expr)` |
+| General value access | Last resolved type via reflection | Standard value
access codegen |
+
+Built-in functions are registered in `BUILTIN_FUNCTIONS` map in
`LALBlockCodegen`.
+
+### Chained def variables
+
+A `def` variable can be initialized from another `def` variable's method chain:
+
+```
+def jwt =
toJson(parsed?.commonProperties?.metadata?.filterMetadataMap?.get("envoy.filters.http.jwt_authn"))
+def payload = jwt?.getAsJsonObject("payload")
+tag 'email': payload?.get("email")?.getAsString()
+```
+
+The general value access path in `generateDefStatement()` recognizes `jwt` as
a def variable,
+delegates to `generateDefVarChain()` which uses reflection to resolve the
chain, and
+`genCtx.lastResolvedType` captures the resolved type (`JsonObject` in this
case).
+
+### Runtime helpers
+
+`LalRuntimeHelper` provides `toJsonObject()` and `toJsonArray()` overloads:
+
+| Method | Input type | Conversion |
+|---|---|---|
+| `toJsonObject(String)` | JSON string |
`JsonParser.parseString().getAsJsonObject()` |
+| `toJsonObject(Map)` | Map (from JSON/YAML parser) | Recursive Gson
conversion |
+| `toJsonObject(Struct)` | Protobuf `Struct` | Recursive field conversion
preserving nested structures |
+| `toJsonObject(Object)` | Any (fallback) | Delegates to above based on
runtime type |
+| `toJsonArray(String)` | JSON array string |
`JsonParser.parseString().getAsJsonArray()` |
+| `toJsonArray(Object)` | Any (fallback) | String fallback |
+
Review Comment:
Fixed in d990305. Map support is now implemented — docs and code are
consistent.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]