Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions docs/en/operation/dynamic-code-generation-debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,43 @@ Reading this:
4. **Use the statement number** (after the last `:`) as a rough indicator of which operation within the
generated method failed. Dump the class (see above) and use `javap -v` to see the exact mapping.

## Generating All DSL Classes Offline

You can compile every DSL script from the source tree without starting the OAP server.
This is useful for verifying that all scripts are syntactically valid after editing, or for
batch-inspecting the generated bytecode with `javap` or an IDE decompiler.

The `DSLClassGeneratorTest` in the `server-starter` module compiles all OAL, MAL, LAL, and
Hierarchy scripts and dumps the `.class` files to `target/generated-dsl-classes/`.

```shell
# From the project root (requires a prior build: ./mvnw -Pbackend install -Dmaven.test.skip)
./mvnw test -pl oap-server/server-starter \
-Dtest="DSLClassGeneratorTest#generateAllDSLClasses" \
-Dcheckstyle.skip
```

### Output

```
oap-server/server-starter/target/generated-dsl-classes/
├── oal/ ← Metrics, MetricsBuilder, Dispatcher classes
├── mal/ ← MAL expression and filter classes
├── lal/ ← LAL expression classes
└── hierarchy/ ← Hierarchy rule classes
```

The test fails if any script cannot be compiled and prints the list of failures.

### What It Covers

| DSL | Scripts | Source Directory |
|-----|---------|-----------------|
| OAL | All 9 `.oal` files (core, java-agent, dotnet-agent, browser, mesh, tcp, ebpf, cilium, disable) | `src/main/resources/oal/` |
| MAL | All YAML files across 4 directories | `src/main/resources/{otel-rules,meter-analyzer-config,envoy-metrics-rules,log-mal-rules}/` |
| LAL | All YAML files, with SPI-resolved `inputType`/`outputType` | `src/main/resources/lal/` |
| Hierarchy | All `auto-matching-rules` entries | `src/main/resources/hierarchy-definition.yml` |

## Common Error Patterns

### OAL Compilation Failure
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package org.apache.skywalking.oap.server.analyzer.provider.trace.parser.listener;

import java.util.Optional;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -75,7 +76,8 @@ public String name() {
}

@Override
public void init(final LogData logData, final NamingControl namingControl) {
public void init(final LogData logData, final Optional<Object> extraLog,
final NamingControl namingControl) {
this.namingControl = namingControl;
// Only populate fields not already set by the LAL extractor.
if (this.serviceName == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import java.util.Optional;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -99,7 +100,8 @@ public String name() {
}

@Override
public void init(final LogData logData, final NamingControl namingControl) {
public void init(final LogData logData, final Optional<Object> extraLog,
final NamingControl namingControl) {
this.namingControl = namingControl;
// Only populate fields not already set by the LAL extractor.
if (this.traceId == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,18 @@ static void generateMetricsInline(
static void generateTagAssignment(final StringBuilder sb,
final LALScriptModel.TagAssignment tag,
final LALClassGenerator.GenCtx genCtx) {
// tag assignments are only supported on LogBuilder (the default output type).
// Other output types (e.g. SampledTraceBuilder, DatabaseSlowStatementBuilder)
// do not carry tags — fail at compile time instead of silently dropping them.
if (genCtx.outputType != null
&& !org.apache.skywalking.oap.server.core.source.LogBuilder.class
.isAssignableFrom(genCtx.outputType)) {
throw new IllegalArgumentException(
"LAL 'tag' assignments are only supported when outputType is LogBuilder"
+ " (or default), but the resolved outputType is "
+ genCtx.outputType.getName()
+ ". Remove the 'tag' statements or change the outputType.");
}
for (final Map.Entry<String, LALScriptModel.TagValue> entry
: tag.getTags().entrySet()) {
sb.append(" _o.addTag(\"")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

package org.apache.skywalking.oap.log.analyzer.v2.dsl;

import com.google.protobuf.Message;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -85,12 +84,12 @@ public LogData.Builder log() {
return (LogData.Builder) getProperty(KEY_LOG);
}

public ExecutionContext extraLog(final Message extraLog) {
public ExecutionContext extraLog(final Object extraLog) {
parsed().extraLog = extraLog;
return this;
}

public Message extraLog() {
public Object extraLog() {
return parsed().getExtraLog();
}

Expand Down Expand Up @@ -179,6 +178,6 @@ public static class Parsed {
private Map<String, Object> map;

@Getter
private Message extraLog;
private Object extraLog;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
package org.apache.skywalking.oap.log.analyzer.v2.dsl.spec.filter;

import com.fasterxml.jackson.core.type.TypeReference;
import com.google.protobuf.Message;
import com.google.protobuf.TextFormat;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.skywalking.apm.network.logging.v3.LogData;
import org.apache.skywalking.oap.log.analyzer.v2.dsl.ExecutionContext;
import org.apache.skywalking.oap.log.analyzer.v2.dsl.spec.AbstractSpec;
Expand Down Expand Up @@ -172,7 +172,7 @@ public void sink(final ExecutionContext ctx) {

private void doSink(final ExecutionContext ctx) {
final LogData.Builder logData = ctx.log();
final Message extraLog = ctx.extraLog();
final Optional<Object> extraLog = Optional.ofNullable(ctx.extraLog());

if (!ctx.shouldSave()) {
if (LOGGER.isDebugEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,10 @@

import java.util.List;
import org.apache.skywalking.oap.log.analyzer.v2.provider.log.listener.LogAnalysisListenerFactory;
import org.apache.skywalking.oap.log.analyzer.v2.provider.log.listener.LogSinkListenerFactory;

public interface ILogAnalysisListenerManager {

void addListenerFactory(LogAnalysisListenerFactory factory);

List<LogAnalysisListenerFactory> getLogAnalysisListenerFactories();

void addSinkListenerFactory(LogSinkListenerFactory factory);

List<LogSinkListenerFactory> getSinkListenerFactory();
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

package org.apache.skywalking.oap.log.analyzer.v2.provider.log;

import com.google.protobuf.Message;
import java.util.Optional;
import org.apache.skywalking.apm.network.logging.v3.LogData;
import org.apache.skywalking.oap.server.library.module.Service;

Expand All @@ -26,9 +26,9 @@
*/
public interface ILogAnalyzerService extends Service {

void doAnalysis(LogData.Builder log, Message extraLog);
void doAnalysis(LogData.Builder log, Optional<Object> extraLog);

default void doAnalysis(LogData logData, Message extraLog) {
default void doAnalysis(LogData logData, Optional<Object> extraLog) {
doAnalysis(logData.toBuilder(), extraLog);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,16 @@

package org.apache.skywalking.oap.log.analyzer.v2.provider.log;

import com.google.protobuf.Message;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import lombok.RequiredArgsConstructor;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.apache.skywalking.apm.network.logging.v3.LogData;
import org.apache.skywalking.oap.log.analyzer.v2.provider.log.listener.LogAnalysisListener;
import org.apache.skywalking.oap.server.core.UnexpectedException;
import org.apache.skywalking.oap.server.core.analysis.Layer;
import org.apache.skywalking.oap.server.library.util.StringUtil;
import org.apache.skywalking.oap.log.analyzer.v2.provider.LogAnalyzerModuleConfig;
import org.apache.skywalking.oap.log.analyzer.v2.provider.log.listener.LogAnalysisListener;
import org.apache.skywalking.oap.server.library.module.ModuleManager;

/**
* Entry point for log analysis. Created per-request by the log receiver.
Expand All @@ -53,15 +49,16 @@
* </ol>
*/
@Slf4j
@RequiredArgsConstructor
public class LogAnalyzer {
private final ModuleManager moduleManager;
private final LogAnalyzerModuleConfig moduleConfig;
private final ILogAnalysisListenerManager factoryManager;

public LogAnalyzer(final ILogAnalysisListenerManager factoryManager) {
this.factoryManager = factoryManager;
}

private final List<LogAnalysisListener> listeners = new ArrayList<>();

public void doAnalysis(LogData.Builder builder, Message extraLog) {
public void doAnalysis(LogData.Builder builder, Optional<Object> extraLog) {
if (StringUtil.isEmpty(builder.getService())) {
// If the service name is empty, the log will be ignored.
log.debug("The log is ignored because the Service name is empty");
Expand Down Expand Up @@ -89,7 +86,7 @@ public void doAnalysis(LogData.Builder builder, Message extraLog) {
notifyAnalysisListenerToBuild();
}

private void notifyAnalysisListener(LogData.Builder builder, final Message extraLog) {
private void notifyAnalysisListener(LogData.Builder builder, final Optional<Object> extraLog) {
listeners.forEach(listener -> listener.parse(builder, extraLog));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,24 @@

package org.apache.skywalking.oap.log.analyzer.v2.provider.log;

import com.google.protobuf.Message;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.apache.skywalking.apm.network.logging.v3.LogData;
import org.apache.skywalking.oap.log.analyzer.v2.provider.LogAnalyzerModuleConfig;
import org.apache.skywalking.oap.log.analyzer.v2.provider.log.listener.LogAnalysisListenerFactory;
import org.apache.skywalking.oap.log.analyzer.v2.provider.log.listener.LogSinkListenerFactory;
import org.apache.skywalking.oap.server.library.module.ModuleManager;

@RequiredArgsConstructor
public class LogAnalyzerServiceImpl implements ILogAnalyzerService, ILogAnalysisListenerManager {
private final ModuleManager moduleManager;
private final LogAnalyzerModuleConfig moduleConfig;
private final List<LogAnalysisListenerFactory> analysisListenerFactories = new ArrayList<>();
private final List<LogSinkListenerFactory> sinkListenerFactories = new ArrayList<>();

@Override
public void doAnalysis(final LogData.Builder log, Message extraLog) {
LogAnalyzer analyzer = new LogAnalyzer(moduleManager, moduleConfig, this);
public void doAnalysis(final LogData.Builder log, Optional<Object> extraLog) {
LogAnalyzer analyzer = new LogAnalyzer(this);
analyzer.doAnalysis(log, extraLog);
}

Expand All @@ -49,14 +47,4 @@ public void addListenerFactory(final LogAnalysisListenerFactory factory) {
public List<LogAnalysisListenerFactory> getLogAnalysisListenerFactories() {
return analysisListenerFactories;
}

@Override
public void addSinkListenerFactory(LogSinkListenerFactory factory) {
sinkListenerFactories.add(factory);
}

@Override
public List<LogSinkListenerFactory> getSinkListenerFactory() {
return sinkListenerFactories;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

package org.apache.skywalking.oap.log.analyzer.v2.provider.log.listener;

import com.google.protobuf.Message;
import java.util.Optional;
import org.apache.skywalking.apm.network.logging.v3.LogData;

/**
Expand All @@ -31,7 +31,11 @@ public interface LogAnalysisListener {

/**
* Parse the raw data from the probe.
* @param logData log metadata (service, layer, timestamp, etc.)
* @param extraLog the actual input object whose type matches
* {@code LALSourceTypeProvider#inputType()} — may be {@code null}
* for standard logs where LogData is the sole input
* @return {@code this} for chaining.
*/
LogAnalysisListener parse(LogData.Builder logData, final Message extraLog);
LogAnalysisListener parse(LogData.Builder logData, Optional<Object> extraLog);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@

package org.apache.skywalking.oap.log.analyzer.v2.provider.log.listener;

import com.google.protobuf.Message;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -38,7 +38,6 @@

import org.apache.skywalking.oap.server.core.analysis.Layer;
import org.apache.skywalking.oap.server.core.source.LALOutputBuilder;
import org.apache.skywalking.oap.server.core.source.Source;
import org.apache.skywalking.oap.server.library.module.ModuleManager;
import org.apache.skywalking.oap.server.library.module.ModuleStartException;

Expand Down Expand Up @@ -78,15 +77,34 @@ public void build() {

@Override
public LogAnalysisListener parse(final LogData.Builder logData,
final Message extraLog) {
final Optional<Object> extraLog) {
final LogData logDataSnapshot = logData.build();
contexts = new ArrayList<>(dsls.size());
for (int i = 0; i < dsls.size(); i++) {
contexts.add(new ExecutionContext().log(logDataSnapshot).extraLog(extraLog));
contexts.add(new ExecutionContext().log(logDataSnapshot).extraLog(extraLog.orElse(null)));
}
return this;
}

/**
* Eagerly compiles all LAL rules at startup and groups the resulting
* {@link DSL} instances by telemetry layer and rule name.
*
* <p>{@code dsls} structure: {@code Layer -> (ruleName -> DSL)}.
* <ul>
* <li><b>Outer key</b> ({@link Layer}): the telemetry layer declared in
* the YAML rule (e.g., {@code GENERAL}, {@code MESH}).</li>
* <li><b>Inner key</b> ({@code String}): the rule {@code name} field
* from the YAML config (e.g., {@code "default"}, {@code "envoy-als"}).
* Must be unique within a layer.</li>
* <li><b>Value</b> ({@link DSL}): a compiled LAL expression plus its
* runtime {@link org.apache.skywalking.oap.log.analyzer.v2.dsl.spec.filter.FilterSpec},
* ready to evaluate incoming logs.</li>
* </ul>
*
* <p>At runtime, {@link #create(Layer)} returns a {@link LogFilterListener}
* containing all DSL instances for the requested layer.
*/
public static class Factory implements LogAnalysisListenerFactory {
private final Map<Layer, Map<String, DSL>> dsls;

Expand Down Expand Up @@ -194,7 +212,7 @@ private static Class<?> resolveOutputType(
}
// Fall back to SPI default for the layer
if (spiProvider != null) {
final Class<? extends Source> spiOutput = spiProvider.outputType();
final Class<?> spiOutput = spiProvider.outputType();
if (spiOutput != null) {
return spiOutput;
}
Expand Down
Loading
Loading