This is an automated email from the ASF dual-hosted git repository.
wu-sheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git
The following commit(s) were added to refs/heads/main by this push:
new cb479c0 live-debug: show the LAL drop reason (#92)
cb479c0 is described below
commit cb479c06680fa0b39422ef663096babe66554f86
Author: 吴晟 Wu Sheng <[email protected]>
AuthorDate: Fri Jul 3 14:33:17 2026 +0800
live-debug: show the LAL drop reason (#92)
---
.../src/features/operate/live-debug/DebugLal.vue | 4 +++
.../ui/src/features/operate/live-debug/LalCell.vue | 32 ++++++++++++++++++++++
.../features/operate/live-debug/LalCellPopout.vue | 32 ++++++++++++++++++++++
apps/ui/src/i18n/locales/en.json | 1 +
apps/ui/src/i18n/locales/zh-CN.json | 1 +
packages/api-client/src/dsl-debugging.ts | 8 +++++-
6 files changed, 77 insertions(+), 1 deletion(-)
diff --git a/apps/ui/src/features/operate/live-debug/DebugLal.vue
b/apps/ui/src/features/operate/live-debug/DebugLal.vue
index 29c7f3f..16b3c07 100644
--- a/apps/ui/src/features/operate/live-debug/DebugLal.vue
+++ b/apps/ui/src/features/operate/live-debug/DebugLal.vue
@@ -428,6 +428,7 @@ const popoutComparable = ref<
>([]);
const popoutDslLines = ref<string[]>([]);
const popoutCurrentLine = ref<number>(0);
+const popoutReason = ref<string | null>(null);
/** Brace-match a top-level DSL block (`extractor {` … `}`) and return its
* 1-based line span, or null when the opener isn't present. */
@@ -467,6 +468,7 @@ function openCellPopout(cell: LalCellData, step: LalStep,
node: LalNodeView): vo
popoutJson.value = fullJson(cell.payload, step.type);
popoutDslLines.value = sourceDslLines.value;
popoutCurrentLine.value = stepLines(step).start;
+ popoutReason.value = cell.sample.reason ?? null;
// Comparable = the record's other same-format (builder) cells. Per-
// statement snapshots map to their DSL line; the post-extractor /
// output snapshots map to their whole block range, so the captured
@@ -970,6 +972,7 @@ function recordTitle(view: LalRecordView): string {
<LalCell
:step-type="step.type"
:payload="cellAt(node, step, rv.recIdx)?.payload ?? null"
+ :reason="cellAt(node, step, rv.recIdx)?.sample.reason ??
null"
/>
</template>
</div>
@@ -985,6 +988,7 @@ function recordTitle(view: LalRecordView): string {
:title="popoutTitle"
:script="popoutScript"
:json="popoutJson"
+ :reason="popoutReason"
:comparable="popoutComparable"
:dsl-lines="popoutDslLines"
:current-line="popoutCurrentLine"
diff --git a/apps/ui/src/features/operate/live-debug/LalCell.vue
b/apps/ui/src/features/operate/live-debug/LalCell.vue
index b85408f..5a96635 100644
--- a/apps/ui/src/features/operate/live-debug/LalCell.vue
+++ b/apps/ui/src/features/operate/live-debug/LalCell.vue
@@ -40,6 +40,9 @@ import {
const props = defineProps<{
stepType: SampleType;
payload: LalSamplePayload | null;
+ /** Why this step stopped the pipeline (parse failure, non-matching regexp,
…),
+ * or null/undefined when it continued or the OAP version doesn't report
it. */
+ reason?: string | null;
/** When set (the cell popout), body/content shows its complete pretty-
* printed value instead of the 80-char dense-matrix preview. */
full?: boolean;
@@ -119,6 +122,10 @@ const outputKvs = computed(() =>
outputEntries(props.payload, props.full));
</div>
</template>
<div v-if="props.payload?.aborted" class="lal__abort">{{ t('aborted')
}}</div>
+ <div v-if="props.reason" class="lal__reason">
+ <span class="lal__reasonlabel">{{ t('drop reason') }}</span>
+ <span class="lal__reasontext">{{ props.reason }}</span>
+ </div>
</template>
<style scoped>
@@ -229,4 +236,29 @@ const outputKvs = computed(() =>
outputEntries(props.payload, props.full));
text-transform: uppercase;
letter-spacing: var(--sw-ls-caps);
}
+
+.lal__reason {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ border-left: 2px solid var(--rr-warn, #d6a96d);
+ padding-left: 6px;
+}
+
+.lal__reasonlabel {
+ color: var(--rr-warn, #d6a96d);
+ font-size: var(--sw-fs-xs);
+ font-weight: var(--sw-fw-bold);
+ text-transform: uppercase;
+ letter-spacing: var(--sw-ls-caps);
+}
+
+.lal__reasontext {
+ color: var(--rr-ink2);
+ font-family: var(--rr-font-mono);
+ font-size: var(--sw-fs-xs);
+ line-height: 1.4;
+ word-break: break-all;
+ white-space: pre-wrap;
+}
</style>
diff --git a/apps/ui/src/features/operate/live-debug/LalCellPopout.vue
b/apps/ui/src/features/operate/live-debug/LalCellPopout.vue
index 2fe7fcd..cfc092d 100644
--- a/apps/ui/src/features/operate/live-debug/LalCellPopout.vue
+++ b/apps/ui/src/features/operate/live-debug/LalCellPopout.vue
@@ -40,6 +40,8 @@ const props = defineProps<{
script: string;
/** The opened cell's complete payload, pretty JSON. */
json: string;
+ /** Why this step stopped the pipeline, or null when it continued. */
+ reason?: string | null;
/** Same-format sibling cells. `lineStart..lineEnd` is the DSL span
* (equal for per-statement steps, the block range for block steps). */
comparable: { label: string; script: string; json: string; lineStart:
number; lineEnd: number }[];
@@ -114,6 +116,11 @@ function clearCompare(): void {
<div class="lpop">
<pre v-if="script" class="lpop__script">{{ script }}</pre>
+ <div v-if="reason" class="lpop__reason">
+ <span class="lpop__reasonlabel">{{ t('drop reason') }}</span>
+ <span class="lpop__reasontext">{{ reason }}</span>
+ </div>
+
<div v-if="comparable.length > 0" class="lpop__bar">
<span class="lpop__lbl">{{ t('compare with') }}</span>
<button
@@ -217,6 +224,31 @@ function clearCompare(): void {
flex-shrink: 0;
}
+.lpop__reason {
+ display: flex;
+ flex-direction: column;
+ gap: 3px;
+ padding: 6px 9px;
+ border-left: 2px solid var(--rr-warn, #d6a96d);
+ background: var(--rr-bg);
+ flex-shrink: 0;
+}
+.lpop__reasonlabel {
+ color: var(--rr-warn, #d6a96d);
+ font-size: var(--sw-fs-xs);
+ font-weight: var(--sw-fw-bold);
+ text-transform: uppercase;
+ letter-spacing: var(--sw-ls-caps);
+}
+.lpop__reasontext {
+ color: var(--rr-ink);
+ font-family: var(--rr-font-mono);
+ font-size: var(--sw-fs-sm);
+ line-height: 1.5;
+ white-space: pre-wrap;
+ word-break: break-word;
+}
+
.lpop__bar {
display: flex;
align-items: center;
diff --git a/apps/ui/src/i18n/locales/en.json b/apps/ui/src/i18n/locales/en.json
index 667ab15..b3e44fa 100644
--- a/apps/ui/src/i18n/locales/en.json
+++ b/apps/ui/src/i18n/locales/en.json
@@ -335,6 +335,7 @@
"carried": "carried",
"+ added": "+ added",
"aborted": "aborted",
+ "drop reason": "drop reason",
"file": "file",
"select an .oal file…": "select an .oal file…",
"metric": "metric",
diff --git a/apps/ui/src/i18n/locales/zh-CN.json
b/apps/ui/src/i18n/locales/zh-CN.json
index a6e736c..716866a 100644
--- a/apps/ui/src/i18n/locales/zh-CN.json
+++ b/apps/ui/src/i18n/locales/zh-CN.json
@@ -331,6 +331,7 @@
"carried": "继承",
"+ added": "+ 新增",
"aborted": "已中止",
+ "drop reason": "丢弃原因",
"file": "文件",
"select an .oal file…": "选择一个 .oal 文件…",
"metric": "指标",
diff --git a/packages/api-client/src/dsl-debugging.ts
b/packages/api-client/src/dsl-debugging.ts
index f40404a..0489786 100644
--- a/packages/api-client/src/dsl-debugging.ts
+++ b/packages/api-client/src/dsl-debugging.ts
@@ -432,13 +432,19 @@ export type SamplePayload =
* - `payload`: per-DSL shape — see the per-DSL types above.
* - `sourceLine`: 1-based line number in the rule body. Omitted when
* 0 / not applicable (block-level LAL probes, MAL chain stages on
- * a one-liner rule). */
+ * a one-liner rule).
+ * - `reason`: human-readable explanation of WHY this step stopped the
+ * pipeline (a parse exception, a non-matching regexp, an input-type
+ * mismatch), paired with `continueOn: false`. Optional — LAL-only
+ * today, and absent from OAP versions before it was added, so the
+ * UI must treat its absence as "no reason available". */
export interface SessionSample {
type: SampleType;
sourceText: string;
continueOn: boolean;
payload: SamplePayload;
sourceLine?: number;
+ reason?: string;
}
/** Catalog-specific structured rule metadata. The recorder fills in