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

Reply via email to