This is an automated email from the ASF dual-hosted git repository.
wu-sheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-website.git
The following commit(s) were added to refs/heads/master by this push:
new 5b163a75ece docs(blog): add Chinese Horizon UI translations for post
1-8 (#871)
5b163a75ece is described below
commit 5b163a75ece22b4864fb9964ae39b39463354195
Author: 吴晟 Wu Sheng <[email protected]>
AuthorDate: Mon Jun 29 08:50:49 2026 +0800
docs(blog): add Chinese Horizon UI translations for post 1-8 (#871)
---
.../index.md | 91 ++++++++++++
.../index.md | 85 ++++++++++++
.../index.md | 74 ++++++++++
.../index.md | 152 +++++++++++++++++++++
.../index.md | 91 ++++++++++++
.../2026-06-22-horizon-ui-trace-explorer/index.md | 92 +++++++++++++
.../index.md | 69 ++++++++++
.../zh/2026-06-23-horizon-ui-log-explorer/index.md | 67 +++++++++
8 files changed, 721 insertions(+)
diff --git a/content/zh/2026-06-21-horizon-ui-dashboards-and-mqe/index.md
b/content/zh/2026-06-21-horizon-ui-dashboards-and-mqe/index.md
new file mode 100644
index 00000000000..188e9addc75
--- /dev/null
+++ b/content/zh/2026-06-21-horizon-ui-dashboards-and-mqe/index.md
@@ -0,0 +1,91 @@
+---
+title: "认识 Horizon UI · 2/17:动态仪表盘、MQE 与指标格式化"
+date: 2026-06-21
+author: 吴晟
+description: "Horizon UI 系列第二篇:仪表盘如何由 MQE 表达式驱动,如何按当前服务、实例或 endpoint
自动取舍组件并跳过无关查询,以及如何把编码、耗时和大体量指标值格式化成适合排查时阅读的值。"
+tags:
+ - Metrics
+ - Cloud Native
+---
+
+*译自英文原文:[Meet Horizon UI · 2/17: Dashboards That Adapt — MQE, Smart Widgets,
and Numbers Humans Can Read](/blog/2026-06-21-horizon-ui-dashboards-and-mqe/)。*
+
+这是 [Apache SkyWalking Horizon
UI](/zh/2026-06-21-skywalking-horizon-ui-introduction/) 系列的第二篇。第一篇介绍了控制台和按
Layer 组织的导航;这一篇讲你每天最常停留的界面:**仪表盘**。
+
+Horizon 里的每个仪表盘,本质上都使用同一套机制:一个组件网格,每个组件都是一条由 BFF 解析并发往 OAP 的 **MQE
表达式**。它值得单独写一篇,不只是因为能展示指标值,还因为它会处理很多实际问题:不适合当前服务、实例或 endpoint
的组件会被隐藏,相关查询也完全不会发出;编码值、耗时和原始字节数会按场景格式化;页面上所有图表都绑定到同一个游标;你还可以从一条慢 SQL
记录直接跳到产生它的 Trace。下面逐项看。
+
+## 每个组件都是一条 MQE 表达式
+
+Layer 仪表盘是一个紧凑的 **12 列网格**:行高 120px,空隙会自动回填,避免墙一样的组件块出现洞;宽度低于约 1100px
时会折叠成单列。每个格子是五类组件之一,而组件类型取决于 MQE 表达式返回数据的 *形状*:
+
+- **`card`**:表达式收敛为单个标量,比如 `latest(...)`、`avg(...)`、`service_sla/100`。显示一个主指标值。
+- **`line`**:时间序列;每个表达式一条线,混合单位时可以使用双 Y 轴,比如左侧吞吐、右侧时延。
+- **`top`**:来自 `top_n(endpoint_cpm, 20, des)` 的排行列表,带一个小标签切换器,可以在 Traffic /
Slow / Successful Rate 之间切换排序。
+- **`record`**:记录型输出,比如慢数据库语句或慢缓存命令:文本行加数值。
+- **`table`**:带标签的 `latest(...)` 指标,每组 label 一个表格行,比如每个服务的 pod phase、node
condition、deployment replicas。
+
+你不需要手工选择图表类型;写好 MQE,合适的组件会自己渲染。而且 service、instance、endpoint
这些层级使用同一套网格系统。Layer 模板里的 `dashboards.<scope>` map
会为不同页面配置不同组件集合,所以向下钻取时,仪表盘会切到对应作用域。(这些都运行在[第一篇](/zh/2026-06-21-skywalking-horizon-ui-introduction/)介绍的
BFF 层;浏览器不直接访问 OAP。)
+
+## 根据当前对象决定显示哪些组件
+
+这个设计会明显改变仪表盘的使用感受。组件可以带一个 **`visibleWhen`**
条件。如果条件不成立,组件不渲染;更关键的是,**它的查询也不会执行**。
+
+条件有两类:
+
+- **MQE metric**:只有表达式 *有值* 时展示组件(`op: exists`),或者只有值超过阈值时展示(`gt` /
`lt`)。组件可以拿自己的指标做自我开关:JVM 组件带 `"visibleWhen": { "kind": "mqe", "expression":
"instance_jvm_cpu", "op": "exists" }`,所以它们会出现在 Java 实例上,在 Go 实例上消失。
+- **Entity attribute**:在 Instance 作用域上,根据选中实例的属性开关,比如 `language eq
JAVA`,或者某个属性是否存在。
+
+因为条件在 **服务端** 计算,非 JVM 实例不只是把 JVM 格子藏起来;BFF 根本不会把这些查询发给 OAP。用同一个 Instance
仪表盘打开一个 JVM 服务和一个非 JVM 服务,你看到的是同一个模板根据当前对象自动取舍,而不是两套手工维护的页面。
+
+
+图 1:JVM 实例上会渲染 JVM 组件,因为它们的 `visibleWhen` 条件成立。</br>
+
+
+图 2:同一个仪表盘打开在 Go 实例上,JVM 组件不存在,查询也没有执行。同一个模板,会根据当前对象调整内容。</br>
+
+## 指标值的显示格式
+
+原始指标不一定适合直接给人看。Horizon 的组件会处理三类过去常让运维人员在脑子里换算的值:
+
+- **`enum`**:用 value→label map 把编码型 gauge 变成文字。`1/0` 成功指标会显示成 **`OK`** /
**`Failed`**,而不是裸数字。label 可以按 locale 翻译。
+- **`duration`**:以秒为单位的指标会显示成人能理解的时间差,比如 **`5m 20s ago`**;在坐标轴上会压缩成 `5m` /
`2h`。
+- **SI suffixes**:图表坐标轴和 tooltip 上的大数会显示成
**`45.1k`**、**`1.34M`**、**`2.5G`**,而不是 `4.51e4`。坐标轴刻度和 hover 值使用同一套写法。
+
+
+图 3:高密度字节数和计数序列使用紧凑 SI 后缀,坐标轴和 tooltip 保持一致。</br>
+
+
+图 4:`enum` 和 `duration` 格式在 BanyanDB 生命周期卡片上的效果:`OK` 代替 `1`,"5m 20s ago"
代替秒数。</br>
+
+## 按同一条时间线阅读整页图表
+
+页面上所有 `line` 图共享 **同一个 hover 游标**。指向吞吐图上的第 32 分钟,时延图、错误率图和每个 sparkline 格子上的第
32 分钟也会一起亮起。这个契约在图表 wrapper
层强制执行,任何组件都不能退出,所以页面读起来是一组围绕同一时刻协同的视图,而不是十几个互不相关的图。多序列 tooltip 是固定对齐的表格,展示每条序列的
**title**(不会显示原始 MQE),所有值在同一列右对齐。
+
+
+图 5:一个游标跨过页面上所有折线图,所以你可以在所有图上同时读同一瞬间。</br>
+
+## 从慢记录直接打开 Trace
+
+`record` 组件,比如 Slow Statements、Slow Commands、Slow Database
Statements,是采样记录列表。每一行如果带 trace id,行首会出现一个 **jump-to-trace** 图标,点击即可打开对应 Trace
的瀑布图。它按 **trace id** 解析,而不是按 Layer 解析,这点很重要:Virtual Database / Cache / MQ 服务上的
Slow Statements 属于另一个 Layer 上的 *caller*,虚拟目标 Layer 自己没有 traces 标签页,但跳转仍然能打开正确
Trace。语句文本本身也支持 **点击复制**。
+
+
+图 6:从慢语句跳到执行它的 Trace。按 trace id 解析,所以即使虚拟 Layer 自己没有 traces 标签页也能工作。</br>
+
+## 固定多个对象做对比
+
+有时只看一个对象不够。Horizon 允许你 **锁定多个服务、实例或 endpoint,甚至跨服务锁定,并在当前页面内直接比较**。可以从选择器或
instance/endpoint 列表固定对象;当前正在查看的对象始终属于对比组,会标记为
`CURRENT`,并继续驱动顶部信息区。每个固定对象都有自己的颜色。之后每个组件都会就地对比:line 组件为每个对象叠加一条序列,card
为每个对象显示一行,`top` 和 `record` 组件增加按对象切换的标签页,table 增加 Entity
列。持久的对比栏会保持这组对象,不受底层列表分页或当前查看对象变化影响;每个对象单独发起请求,所以一个对象响应慢,不会把其他对象拖成空白。
+
+
+图 7:锁定对象,甚至跨服务锁定,所有折线组件都会按颜色叠加;对比栏保持这组对象,CURRENT 对象仍然驱动顶部信息区。</br>
+
+## 时间范围作用于整个仪表盘
+
+顶部时间范围会驱动页面上的所有内容:顶部 KPI 区、组件主体,以及 BanyanDB 分层 hot/warm/cold 存储里 **Cold**
标记相关的整条路径。过去落地页和拓扑路由固定在最近 60 分钟,所以选择 "12 days ago"
后仍然会悄悄展示近期数字;现在时间选择器在所有地方都生效。上游控制变化时,每个依赖它的格子会明确重置并显示 "Reading data..."
提示,而不是在加载动画下面留着旧值。
+
+## 后续阅读
+
+需要强调的是,这是一套系统。同样的五类组件、同样的 MQE、同样的条件开关,会渲染每个 Layer 的仪表盘:上面的 JVM 面板、BanyanDB
生命周期卡片、mesh 服务上的百分位时延,以及专门为 Envoy AI Gateway(token
throughput、time-to-first-token)或 GenAI virtual layer(per-model estimated
cost)设计的面板。不同 Layer 之间变化的是 MQE,不是机制。
+
+上面讲的是 *查看* 体验。每个组件的 MQE、`visibleWhen` 条件、格式,以及各作用域的网格,都可以从 **Layer
dashboards** 管理界面编辑。但这个创作流程(draft → preview → publish,以及可内联/展开的 MQE
编辑器)会在后续文章里单独展开。字段级参考可以看文档里的 [dashboard
widgets](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/components/dashboard-widgets/)
和
[charts](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/components/charts/)。
+
+下一篇:**topology and service dependency**,把 Horizon 在这里用图表展示的同一批数据,画成一张可以走进去看的地图。
diff --git a/content/zh/2026-06-21-horizon-ui-deployment-and-banyandb/index.md
b/content/zh/2026-06-21-horizon-ui-deployment-and-banyandb/index.md
new file mode 100644
index 00000000000..8607bb1d5c2
--- /dev/null
+++ b/content/zh/2026-06-21-horizon-ui-deployment-and-banyandb/index.md
@@ -0,0 +1,85 @@
+---
+title: "认识 Horizon UI · 4/17:Deployment 标签页与 BanyanDB 自观测"
+date: 2026-06-21
+author: 吴晟
+description: "Horizon UI 系列第四篇:Deployment 标签页如何把拓扑视角收进单个集群服务内部,并以 BanyanDB
为例说明 SkyWalking 如何观测自己的存储引擎。"
+tags:
+ - Cloud Native
+ - Storage
+---
+
+*译自英文原文:[Meet Horizon UI · 4/17: The Deployment Tab & BanyanDB
Self-Observability](/blog/2026-06-21-horizon-ui-deployment-and-banyandb/)。*
+
+这是 [Meet Horizon UI](/zh/2026-06-21-skywalking-horizon-ui-introduction/)
系列的第四篇。[第三篇](/zh/2026-06-21-horizon-ui-topology-and-dependency/)画的是服务 *之间*
的地图。这一篇把视角收回来,画一个集群服务 *内部* 的实例关系,并用它解决 SkyWalking 过去一直没有很好展示的问题:把 **自己的存储引擎**
BanyanDB 按真实集群形态呈现出来。
+
+## Deployment 标签页:查看服务内部实例关系
+
+服务地图回答“谁调用这个服务”。新的按 Layer 配置的 **Deployment** 标签页回答另一个问题:“*这个*
服务是怎么部署的,它自己的实例之间又是怎么通信的?”选择一个服务后,这个标签页会把它的实例画成节点,并画出实例之间的调用关系。你已经在服务地图里见过的平移/缩放画布、健康
ring 节点、边流动动画和每条调用的指标侧栏都会复用,只是作用域收缩到单个服务内部。
+
+它不是一张普通节点图,关键在三点:
+
+- **实例渲染成六边形,并组合成 pod。** 一个 pod 的 **main** container 是完整六边形,**sibling**
containers 会作为更小的六边形贴在它边上。所以主进程和 sidecar 会作为一个单元呈现,跨 pod 的 sidecar
链接也会连到它真正所属的小六边形上。
+- **Pod 按你选择的规则聚到带标签的分组框里。** 规则可以是单个实例属性(role)、多个属性组合(比如 `node_role` +
`node_type`),也可以是名称正则。这样一组混合角色节点会按角色分框展示,而不是堆成一团。
+- **布局是分层的。** 每个 cluster box 会按调用深度摆放 pod:source 在左,被调用对象在右,所以
upstream→downstream 链条从左到右就能看清;拖动任意 pod 后,它所属的 box 会重新流式布局,保持内容都被包住。
+
+边按 **(source-role → target-role)** pair
区分,所以每类链接展示自己的指标,而不是共享一组扁平指标。主指标会直接印在边上,完整指标则进入 **Flows** 子标签页,每种 role-pair
对应一张对齐表。它默认关闭,和服务地图一样,完全从 **Layer dashboards admin → Deployment** 作用域配置。
+
+## 把 BanyanDB 纳入 SkyWalking 自观测
+
+这套机制不是为了多画一种图,它首先服务于一个具体场景。SkyWalking 的原生数据库 **BanyanDB** 是一个集群化、按角色和 tier
组织的系统,而过去 SkyWalking 很难把它作为一个整体观测清楚。新的 **BanyanDB** Layer 位于
**Self-Observability** 下,并配合 OAP 后端 SWIP-15,把通过 BanyanDB FODC proxy
抓到的指标建模成整个部署:
+
+- 整个 **cluster** 是一个 **Cluster**(service);
+- 每个 container 是一个 **Container**(instance),并携带 `container_name` **role** 和
`node_type` **tier** 属性;
+- 每个存储 **Group** 是一个 **endpoint**。
+
+所以其他 Layer 中通用的 Service / Instance / Endpoint 导航结构,在 BanyanDB 这里就变成 **Cluster
/ Container / Group**。叠在上面的 Deployment 标签页,会直接画出这个数据库集群自身。
+
+
+图 1:SkyWalking 观测自己的数据库:BanyanDB 集群按角色和 tier 绘制,并展示 pod 之间的 liaison→data 与
lifecycle→data 边。</br>
+
+### Cluster、Container、Group
+
+每个作用域都有专门设计的仪表盘:
+
+- **Cluster** 仪表盘是整个数据库的总览视图:write / query / error-rate KPI、CPU / memory /
disk capacity、吞吐和错误趋势,以及 **Containers by Role** 表。
+- **Container** 仪表盘会根据选中容器的 **role** 调整内容。每个容器都有 CPU / memory / Go runtime
资源面板;**liaison** 会增加 ingestion、query、gRPC errors、tier-2 publish pipeline 和
write-queue depth;**data** 节点会增加 storage totals、merge / compaction、inverted
index、subscribe queue 和 retention;**lifecycle** sidecar 会显示 migration cycles 和
last-run time / status。角色专属面板按容器 role 属性开关,所以你只会看到当前节点适用的内容。
+- **Group** 仪表盘按 **data model** 拆分:measure、stream、trace、property。因为一个 BanyanDB
group 只存储一个 catalog,只有匹配该模型的面板会渲染:`measure` group 显示 write-rate / query-latency
/ merge 面板,`property` group 显示 index-write / term-search / series 面板,以此类推。
+
+<figure
style="display:flex;flex-wrap:wrap;gap:1.1rem;align-items:center;margin:1.5rem
0;">
+<img src="/screenshots/horizon-0.7.0/p04-deployment-02-cluster-dashboard.webp"
alt="BanyanDB Cluster 仪表盘:write/query/error KPI、CPU/memory/disk capacity,以及
Containers by Role 表。"
style="max-height:320px;max-width:100%;width:auto;border-radius:4px;">
+<figcaption style="flex:1 1
240px;color:#5f6b7a;font-size:.92em;line-height:1.6;">图 2:Cluster
作用域,一眼看完整个数据库,并按角色列出所有容器。</figcaption>
+</figure>
+
+打开 *同一个* Container 仪表盘到两个不同 role 上,最容易看出 role-gating 的效果:
+
+<figure
style="display:flex;flex-wrap:wrap;gap:1.1rem;align-items:center;margin:1.5rem
0;">
+<img
src="/screenshots/horizon-0.7.0/p04-deployment-03-container-dashboard.webp"
alt="data/liaison 节点的 Container 仪表盘,在共享 CPU / memory / Go-runtime 资源面板上方显示角色专属的
ingestion / storage 面板。"
style="max-height:320px;max-width:100%;width:auto;border-radius:4px;">
+<figcaption style="flex:1 1
240px;color:#5f6b7a;font-size:.92em;line-height:1.6;">图 3:data/liaison
Container,在共享资源面板之上展示 ingestion、query、storage 和 compaction 面板。</figcaption>
+</figure>
+
+<figure
style="display:flex;flex-wrap:wrap;gap:1.1rem;align-items:center;margin:1.5rem
0;">
+<img
src="/screenshots/horizon-0.7.0/p04-deployment-03-container-lifecycle-dashboard.webp"
alt="同一个 Container 仪表盘打开在 lifecycle sidecar 上,只展示 migration-cycle 和 last-run
面板。" style="max-height:320px;max-width:100%;width:auto;border-radius:4px;">
+<figcaption style="flex:1 1
240px;color:#5f6b7a;font-size:.92em;line-height:1.6;">图 4:同一个仪表盘打开在 lifecycle
节点上,只显示 migration 面板。同一模板,按 role 开关。</figcaption>
+</figure>
+
+**Group** 作用域则让每个 storage catalog 都有独立页面:
+
+<figure
style="display:flex;flex-wrap:wrap;gap:1.1rem;align-items:center;margin:1.5rem
0;">
+<img src="/screenshots/horizon-0.7.0/p04-deployment-04-group-dashboard.webp"
alt="Group 仪表盘:某个 storage catalog 的按 data model 配置的面板。"
style="max-height:320px;max-width:100%;width:auto;border-radius:4px;">
+<figcaption style="flex:1 1
240px;color:#5f6b7a;font-size:.92em;line-height:1.6;">图 5:Group 作用域,一次查看一个
storage catalog,它的面板按 group 的 data model 开关。</figcaption>
+</figure>
+
+### Role pair 边与 Flows 表
+
+Deployment 标签页中,容器之间的调用边会从 SWIP-15 instance-relation families 中拿到
**role-pair-specific** 指标:**liaison → data** 边展示 write / query / part-sync 吞吐和
p99;**liaison → liaison** 边展示 write-forward 和 control;**lifecycle → data** 边展示
tier-migration volume / rate / p99。每条边最多内联显示三项属于该 pair 的指标,选中边面板保留完整
client-vs-server 拆分,**Flows** 子标签页则把每条边按 role-pair 展开成一组对齐表。
+
+
+图 6:Flows,把同样的 role-pair 边显示成可排序表格,每个 pair 一个区块。</br>
+
+这里有两个前提。边和角色专属面板假设你运行的是一个真实的 **集群化** BanyanDB;单进程 standalone 实例只会显示共享资源和 Go
runtime 面板,其余面板会在集群角色开始上报后亮起来。尤其是 container-to-container 边,还需要 OAP 构建暴露
`SERVICE_INSTANCE_RELATION` 作用域;在那之前,Deployment 标签页仍然会画出完整清单,只是 pod 之间没有边。
+
+## 由配置生成,而不是写死在页面里
+
+上面这些不是一张手工写死的 "BanyanDB 页面"。聚类规则、每个 role 的节点指标、role-pair 边指标,都在 Layer template
中作为一个自包含块存在,可以从 **Layer dashboards admin → Deployment** 作用域编辑,并随模板
export/import 一起携带。它和其他 Layer 背后的配置驱动模型一样,后续文章会完整展开。
+
+下一篇:**3D Infrastructure Map**。同一个部署,以及所有其他 Layer,会进入三维空间,变成一张展示系统全貌的 WebGL 视图。
diff --git a/content/zh/2026-06-21-horizon-ui-topology-and-dependency/index.md
b/content/zh/2026-06-21-horizon-ui-topology-and-dependency/index.md
new file mode 100644
index 00000000000..ecd7085193b
--- /dev/null
+++ b/content/zh/2026-06-21-horizon-ui-topology-and-dependency/index.md
@@ -0,0 +1,74 @@
+---
+title: "认识 Horizon UI · 3/17:拓扑与服务依赖"
+date: 2026-06-21
+author: 吴晟
+description: "Horizon UI 系列第三篇:由模板驱动的拓扑引擎、拓扑降噪、从服务调用下钻到实例、endpoint 依赖图,以及跨
Layer 的 Smartscape 视图。"
+tags:
+ - Cloud Native
+ - Tracing
+---
+
+*译自英文原文:[Meet Horizon UI · 3/17: Topology & Service
Dependency](/blog/2026-06-21-horizon-ui-topology-and-dependency/)。*
+
+这是 [Meet Horizon UI](/zh/2026-06-21-skywalking-horizon-ui-introduction/)
系列的第三篇。[第二篇](/zh/2026-06-21-horizon-ui-dashboards-and-mqe/)讲的是如何从仪表盘数字里理解服务;这一篇讲如何把服务关系画成一张
**地图**:谁调用谁,调用有多重,以及同一个逻辑服务在不同上报 Layer 中是什么样子。
+
+SkyWalking 拓扑背后的调用数据是一份,但 Horizon 不只画一种图。它有每个 Layer
自己的服务地图,可以从单条调用下钻到背后的实例,可以看 endpoint 级依赖图,也可以用跨 Layer 叠加视图把一个服务在不同 Layer
中的形态连起来。它们共用同一套引擎,只是回答的问题不同。(Deployment 标签页,也就是单个集群服务 *内部* 实例的地图,以及 WebGL 3D
地图,都足够大,会在接下来的文章里单独讲。)
+
+## 一套拓扑引擎,适配不同 Layer
+
+打开任意 Layer 的 **Topology** 标签页,你会看到一张从左到右排列的层次化服务地图:`User`
存在时位于最左侧,每个服务按调用深度落在不同列里;同一列内部则保留图遍历到它们时的顺序,所以主调用链可以自上而下读。每个服务都是一个
**六边形节点**,节点上展示的所有东西都来自这个 Layer 的配置,没有写死逻辑:
+
+- 六边形 **边框** 承载节点的 **ring** 指标,用类似 SLA 的健康色带展示(绿色 → 红色);
+- 组件 **图标** 放在六边形内部,和 Trace 瀑布图使用同一套图标,所以 PostgreSQL 节点像 PostgreSQL,Kafka 节点像
Kafka;
+- 节点头部数字,也就是 **center** 指标,带单位显示在六边形 **上方**;
+- 服务 **名称** 显示在节点 **下方**,**secondary** 指标(默认是时延)显示在名称下面;
+- 每条 **边** 都带调用吞吐的 **RPM 标记**,使用服务端指标,缺失时回退到客户端指标。
+
+这里需要强调一点:上面这些都只是 **General Layer 的内置默认配置**。节点的 center / ring / secondary
指标,以及边指标,都位于 **Layer dashboards admin → Topology** 作用域,本质上是带单位和角色的 MQE
表达式。所以你可以把任意槽位指向另一条指标,同一套引擎就会为另一个 Layer 画出不同地图,或者按你的方式重画当前
Layer。(这些选择会像其他模板内容一样,跟着 Layer template 的 export/import 走。)
+
+
+图 1:一套由模板驱动的拓扑引擎:带健康色带的六边形节点、带 RPM 标记的边、真实组件图标;图上的每个指标都按 Layer 配置。</br>
+
+## 过滤拓扑噪声
+
+真实拓扑通常很嘈杂。一张密集地图里会混入 OAP 没法完整识别的推测节点,比如裸露的 `rcmd:80`、未接入探针的地址。Horizon 的
**Filter** 控件可以关掉这些噪声,同时保留真实依赖。它会自动生成一个 **按 Layer 分组** 的过滤维度,展示方式和侧边栏一致。每一行都有
Layer 自己的图标和本地化名称,比如 *Virtual Database*、*Java Agent*,还会有一个 **Others** 分组,用来收纳
OAP 无法分类的节点,以及一个独立的 **User** 开关。取消勾选 *Others* 后,未接入探针的杂点和悬空边会消失,而数据库、缓存和队列(各自的
`VIRTUAL_*` 行)仍然留在图上。过滤在客户端执行,行会在每次刷新时重新推导,所以不会陈旧。
+
+
+图 2:快速降噪,去掉无法解析的 "Others" 节点,同时保留真实依赖。</br>
+
+当某个 Layer 的服务属于 OAP service group,地图上的服务聚焦选择器也会按这些 group 分组。点击 group header 可以
**批量选中或清空该组里的所有服务**,所以你能一次聚焦一张繁忙地图中某个团队负责的那一片。
+
+
+图 3:从地图选择器里一次聚焦整个 service group。</br>
+
+## 从服务调用下钻到实例关系
+
+服务到服务的边是一条聚合调用;它背后是真实实例在和真实实例通信。点击地图上的一条调用并选择 **Instance map →**,Horizon
会画出这层关系:客户端服务的实例在左列,服务端服务的实例在右列,中间是实例级调用,并带有客户端→服务端方向动画。它复用服务地图上的所有能力:健康 ring
节点、每条调用的 client/server 指标侧栏、带 **Open instance dashboard** 的节点 popover,并且按 Layer
自己的词汇标注列名,比如 Kubernetes 上叫 *Pods*,data plane 上叫
*Sidecars*。两个服务选择器会根据关系联动:server 列表来自当前 client 的 callees,client 列表来自当前 server 的
callers;每次改动其中一个,另一个都会重新推导。
+
+
+图 4:从一条聚合调用下钻到背后的实例到实例流量。</br>
+
+## 按 endpoint 追完整请求链
+
+服务拓扑回答“哪些服务调用了这个服务”。**API dependency** 标签页回答更具体的问题:“哪些 *endpoint* 调用了这个
endpoint,它又调用了哪些 endpoint”。选择一个 endpoint 后,图会按方向分列:callers 在左,焦点 endpoint
在中间,callees 在右。它同样用 SLA 色边框、每条边上的 RPM 和时延,以及最重边标签。选中节点后会出现一个 **+** handle,点击后拉入
*它自己* 的 callers 和 callees。这样你可以一跳一跳追链路,不需要一次展开整张图。拖开节点后,外跳链接(**Open
endpoint**、**Service →**)会在新标签页打开,保留你正在探索的图。
+
+
+图 5:按 endpoint 一跳一跳走请求链,每条边都有时延。</br>
+
+## 同一个服务在不同 Layer 中的形态
+
+一个逻辑服务经常会同时通过多个 Layer 上报:General agent、Service Mesh sidecar、mesh data
plane、Kubernetes pod。SkyWalking 从 OAP 10 开始就建模了这种跨 Layer 层级关系;Horizon
做的事情,是让你能从地图上的任何位置直接打开它。选中一个节点后,Horizon 会惰性探测它的 hierarchy。如果这个服务有跨 Layer
对应对象,节点上会贴一个小的 **chevron-stack** 标记。
+
+
+图 6:选中节点上的 chevron-stack 标记。它在选择时惰性探测,只在服务存在跨 Layer 对应对象时显示。</br>
+
+点击这个标记,拓扑会在 **Smartscape** 叠加视图下变暗:焦点节点在原位高亮重绘,它的对应对象按 OAP 的 Layer 顺序纵向展开,请求近端
Layer 在上,基础设施近端 Layer 在下。之后两步点击就能在对应 Layer
中打开任意对象,并完成预选。(叠加视图打开时自动刷新会暂停,避免内容在你眼前移动。)
+
+
+图 7:一个服务在所有上报 Layer 中的样子,通过叠加视图展示跨 Layer hierarchy。</br>
+
+## 后续阅读
+
+这些地图上的每个指标、阈值和边权重,都位于 Layer template 的 `topology`
块里。换句话说,你会用和仪表盘一样的配置驱动方式调整它们,这也是后续文章的主题。字段参考可以看
[layer-template](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/customization/layer-templates/)
里的 topology 文档。
+
+下一篇:**Deployment 标签页和 BanyanDB 自观测**。同样的地图技术会用到服务
*内部*,展示一个集群服务自身实例是如何部署、如何相互通信的。
diff --git a/content/zh/2026-06-21-skywalking-horizon-ui-introduction/index.md
b/content/zh/2026-06-21-skywalking-horizon-ui-introduction/index.md
new file mode 100644
index 00000000000..8b523c3dea7
--- /dev/null
+++ b/content/zh/2026-06-21-skywalking-horizon-ui-introduction/index.md
@@ -0,0 +1,152 @@
+---
+title: "认识 Horizon UI · 1/17:SkyWalking 新一代可观测性控制台"
+date: 2026-06-21
+author: 吴晟
+description: "介绍 Apache SkyWalking Horizon UI:它沿用现有 OAP
后端协议,重新设计前端控制台,让观测、运维、治理和定制回到同一个入口。"
+tags:
+ - Release
+ - Cloud Native
+---
+
+*译自英文原文:[Meet Horizon UI · 1/17: SkyWalking's New Observability
Console](/blog/2026-06-21-skywalking-horizon-ui-introduction/)。*
+
+Apache SkyWalking Horizon UI 是 SkyWalking 的新一代 Web 控制台。它仍然连接你已经在运行的 OAP 后端:同样的
GraphQL 查询协议,同样的 admin REST 接口,同样的 MQE 语言,同样的 `Layer` 概念。换句话说,你可以直接把 Horizon
指向正在运行的 OAP,然后登录使用,不需要改后端。变化集中在这些后端协议之上的整套交互体验。
+
+这是这个系列的第一篇。后续文章会依次介绍仪表盘和指标查询语言、拓扑视图、整个部署的 WebGL 3D
地图、链路和日志探索器、性能剖析、运维界面、访问控制,以及把这些模块串起来的配置化定制。本篇先交代背景:Horizon 是什么,整个 UI
围绕哪一个核心想法构建,以及今天如何把它接到你的 OAP 前面。
+
+Horizon 的主线可以概括为四件事:先 **observe**,看拓扑、链路、日志、五类性能剖析、只读告警,以及每个 Layer 的仪表盘;再
**operate** 这些被观测对象;然后 **govern** 谁可以操作它们;最后在不写 UI 代码的情况下 **customize**
整个控制台。Observe、operate、govern、customize,就是这个系列的主线。我们从每次会话都会看到的地方开始:侧边栏。
+
+## 侧边栏显示当前系统全貌
+
+打开 Horizon,左侧边栏不是手工写死的菜单,而是 OAP 当前上报内容的实时映射。Horizon 会向 OAP 查询有哪些 Layer、哪些
Layer 里有服务,然后只渲染这些内容,并每 60 秒刷新一次。某个 Layer
开始上报,它就出现;安静下来,它就消失。菜单不会和真实状态脱节,因为它直接来自 OAP 当前状态。
+
+
+图 1:Horizon 首页,左侧是实时系统全貌,右侧是跨 Layer 的 Services 概览。</br>
+
+这个侧边栏有几个关键点:
+
+- **实时服务计数。** **Layers** 标题显示当前有多少个 Layer 包含服务,图 1 中是 *13 with services*。展开每个
Layer 后,也能看到它自己的服务数量。这些计数来自同一个服务端目录,整个 UI 共用,并且每分钟刷新一次,所以侧边栏、告警 Layer
标记器和落地页不会因为各自轮询而出现不一致。
+- **按组组织,而不是平铺列表。** Layer 会放在自己的分组下,比如 *Virtual
Targets*、*Istio*、*Kubernetes*、*MQ*。顶部是 **Overviews** 和
**Alarms**,运维和管理区域(Cluster Status、Alerting rules、DSL Management、Users、Roles &
permissions)放在更靠下的位置,并且只对有权限的角色展示。访问控制直接进入菜单生成逻辑,而不是事后再补一层。
+- **不会静默隐藏内容。** OAP 上报的每个 Layer 现在都会出现。即使没有内置模板,也会回退到一个普通的 Service 页面。过去有一份写死的
"hidden layers" 列表,会悄悄隐藏 `BanyanDB` 这样的 Layer;现在这件事被移除了。想隐藏某个 Layer,需要在
`horizon.yaml` 里通过 `layers.excluded` 明确配置。默认值是 `FAAS` 和
`VIRTUAL_GATEWAY`;清空列表就可以展示所有 Layer。
+
+点击任意 Layer,Horizon 会打开它的第一个可用标签页。所有 Layer 都沿用同一条固定路径:
+
+```text
+service → instance → endpoint → topology → trace → logs → profiling
+```
+
+槽位名称会跟随 Layer 的语义变化。图 2 中的 General Service Layer 会把 endpoint 槽位命名为
**API**,增加一个 **API dependency** 视图,并把 **Profiling**
展开成它实际拥有的引擎:Trace、eBPF、pprof(Go)和 Async。某个 Layer
不支持的标签页会在模板里直接关掉,所以你不会打开一个空页面。选中一个服务后,右侧画布就是这个服务的仪表盘:上方是一排
KPI(RPM、Apdex、错误率,每个都有自己的 sparkline),下方的组件网格只查询当前服务相关数据。
+
+
+图 2:展开一个 Layer 后进入完整流程,左侧是标签路径,右侧是选中服务的仪表盘。</br>
+
+## 没有数据时,也要说明原因
+
+一个跟随实时数据变化的控制台,必须能处理“没有数据”的时刻:全新安装、配置不完整的部署,或者刚刚重启的 OAP。Horizon
把这些情况都作为明确状态处理,而不是留给用户一个死胡同。
+
+打开 `/` 时,Horizon 会按顺序跳到一个真实可用的页面:第一个可用的公共 overview 仪表盘;如果没有,就进入第一个有服务的
Layer;只有两者都不存在时,才展示空落地页。真的进入空页面时,它会用明确语言告诉你问题在哪里:**"No data is flowing yet"**
表示还没有任何内容上报,**"No dashboard configured yet"** 表示已经有服务,但没有配置
overview。它会把问题指向数据接入或运维配置,而不是把你丢在一个空网格上。只要有服务开始上报,或者运维人员发布了仪表盘,下一次 60
秒刷新就会把空页面替换成真实页面。
+
+
+图 3:空落地页说明真实原因。这里是服务已经上报,但没有配置 overview,而不是给出一个空仪表盘。</br>
+
+OAP 短暂不可达时,Horizon 也遵循同样思路。如果后端短时间连不上,Horizon 会保留最后一次已知的侧边栏结构,并显示 "OAP
unreachable" 横幅,服务计数标记为未知,直到恢复为止。短暂故障不会看起来像配置突然消失。
+
+当数据正常流入时,落地页就是图 1 里看到的总览页:跨 Layer 概览、按类型拆分的服务卡片(General services、Virtual
databases、caches、MQs、GenAI)、实时拓扑和活跃告警栏。每个 Layer 自己的落地页会按你选择的列真正计算 top-N
服务,不再先截取任意前 25 个再排序;页面还会告诉你 "top N of M",所以截断不会悄悄发生。
+
+长 Layer
名称和深命名空间很常见,所以页面框架需要给内容让出空间:拖动分隔线可以调整侧边栏宽度,双击重置;也可以折叠成一条窄图标栏,把水平空间尽量交给画布。宽度会按浏览器记住。
+
+
+图 4:拖动分隔线加宽侧边栏,长名称不再被截断,宽度会按浏览器记住。</br>
+
+
+图 5:需要最大画布空间时,可以把侧边栏折叠成窄图标栏。</br>
+
+## BFF:Horizon 的服务端入口
+
+过去,SkyWalking Web UI 是浏览器直接访问 OAP。Horizon 在中间加了一小层基础设施:**Backend-for-Frontend
(BFF)**,一个运行在 Node.js 上的 Fastify 服务。它负责提供 UI,并代理所有到 OAP 的调用。
+
+
+*浏览器只访问 BFF;BFF 负责 auth、RBAC、audit、capability probing,以及服务端 i18n / widget
gating,然后代理到 OAP 的 query host(`:12800`)和 admin host(`:17128`)。*
+
+后续文章里很多能力都依赖这一层。认证、基于角色的访问控制和审计都在服务端执行,伪造请求绕不过去。BFF 启动时会探测一次 OAP 的 GraphQL
schema,某个能力不存在时就优雅降级;Horizon 能用同一个构建支持两代 OAP,靠的就是这个机制。它还会每分钟缓存一次服务目录,让整个 UI
对系统全貌持有同一份视图。运维、安全和定制相关的文章会分别展开这些内容;这里先记住一点:现在这里有一个服务端,它承担了实实在在的工作。
+
+## 用 3D 地图查看完整部署
+
+有一个界面值得先提前看,因为它最能表达“后退一步,一次看清全局”的想法:**3D Infrastructure Map**。每个 Layer
的服务都会变成立方体,堆叠到按请求流向排列的层级上,实时流量、告警和调用关系都画在它们之间。拖动即可旋转:
+
+{{< map3d poster="/images/home/horizon-3d-map.png" badge="交互演示 · 示例数据" >}}
+
+后续有一篇专门拆解 3D 地图:层级、告警信标、让健康对象变暗、只突出告警对象的 "Beacon mode",以及配置它的结构化编辑器。现在先把它当成
Horizon 目标的一个缩影:整个部署放在一个视图里,而且是活的。
+
+## 系列后续内容
+
+本篇之后,后续文章会分别介绍 Horizon 的不同模块。可以按任意顺序阅读,每篇也都会链接回这里。它们分成四条主线。
+
+**看见你的数据**
+
+1. **Dashboards & MQE**:只查询当前对象相关数据的组件、指标格式化、同步十字线和多对象对比。
+2. **Topology & service dependency**:一套可为每个 Layer 重绘的拓扑引擎、降噪过滤器和多跳 API
dependency 图。
+3. **The Deployment tab & BanyanDB self-observability**:深入单个集群服务内部的视图,以及
SkyWalking 终于像观测其他对象一样观测自己的数据库。
+4. **The 3D Infrastructure Map**:详细展开上面这个 3D 视图。
+5. **Trace explorer**:在时延散点图上框选慢 Trace,然后用三种方式阅读同一条 Trace。
+6. **Log explorer**:类似 Loki 的日志流,带 facets、top patterns 和结构化内容。
+7. **Browser & RUM monitoring**:前端错误日志,以及把混淆后的栈恢复到原始源码行。
+8. **Five profilers, one flame graph**:trace、async-profiler、eBPF、Go pprof 和
network profiling,统一到同一套工作方式里。
+
+**运维**
+
+9. **Alarms & incident triage**:以 incident 为中心的活跃告警,并带上触发它的图表。
+10. **Runtime rules, live debugging & inspect**:可热加载规则,以及用实时样本逐步调试
OAL(traces)、MAL(metrics)和 LAL(logs)的 Live Debugger。
+11. **Platform & cluster introspection**:在 UI 里查看 OAP 集群健康、最终解析后的配置和数据保留策略。
+
+**治理与安全**
+
+12. **Access control & security**:服务端强制执行的 RBAC、LDAP/AD、审计轨迹和 break-glass,让 UI
可以进入企业部署。
+
+**定制与接入**
+
+13. **Customization: config-driven layer templates**:从 draft 到 publish,不写 UI
代码也能增加一个全新的被监控 Layer。
+14. **Localization**:八种语言的仪表盘,并且可以在实时预览里点击组件完成翻译。
+15. **Getting started & migration**:安装、OAP 版本矩阵,以及如何用 Horizon 替换现有 UI。
+
+## 连接现有 OAP 即可试用
+
+Horizon 可以运行在你已有的 OAP 之上。在今天的 **OAP 10.x** 上,绝大多数功能已经可用:所有仪表盘、拓扑、Trace(原生和
**Zipkin**)、日志、告警,以及五类性能剖析,都通过 OAP 的 query host(`:12800`)渲染。Horizon
的访问控制、审计和主题运行在 BFF 里,不依赖 OAP 版本。需要等待 **OAP 11.0** 的是 *operate*
层:runtime-rule(DSL)管理、Live Debugger、Metrics Inspect、告警规则编辑器、Cluster Status
管理面板,以及把模板编辑发布回 OAP。这些依赖 admin host(`:17128`),即将随 OAP 11.0 发布。Horizon 会按 admin
module 是否存在来探测能力,10.x 不能提供的页面会直接隐藏;完整观测控制台今天就能跑在 10.x 上,迁移到 11.0 后运维工具会自动亮起来。
+
+把 Horizon 指向已有集群即可启动,不需要改后端。仍然是你的部署已经在使用的那个 OAP:
+
+```sh
+docker run -d --name horizon \
+ -p 8081:8081 \
+ -v "$PWD/horizon.yaml:/app/horizon.yaml:ro" \
+ -v horizon-state:/data \
+ ghcr.io/apache/skywalking-horizon-ui:<version>
+```
+
+最小化的 `horizon.yaml` 只需要说明 OAP 在哪里,以及一个可登录的本地用户:
+
+```yaml
+oap:
+ queryUrl: http://<oap-host>:12800
+ adminUrl: http://<oap-host>:17128
+auth:
+ backend: local
+ local:
+ users:
+ - username: admin
+ passwordHash: "$argon2id$v=19$..." # generated, never plaintext
+ roles: [admin]
+```
+
+打开 `http://<host>:8081/`,登录后第一站是 **Cluster Status**,确认 Horizon 和 OAP
能正常通信。之后侧边栏就会填入你的系统全貌。
+
+完整安装路径,包括 binary tarball、Kubernetes、LDAP、TLS 和生产检查清单,请看 [Horizon UI
文档](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/readme/)。左侧菜单里覆盖了安装、兼容性、访问控制、定制、组件和运维。
+
+## 其他要点
+
+- **可以直接接入现有 OAP。** Horizon 是一次从零开始的重写,但保留了所有后端契约:同样的 GraphQL 查询协议、admin REST
接口、MQE 语言和 `Layer` 概念。所以你可以把它指向一个正在运行的集群,不改后端。今天的 **OAP 10.x**
已经能运行完整观测控制台(仪表盘、拓扑、包括 **Zipkin** 在内的 Trace、日志、告警、性能剖析),以及 Horizon BFF
侧的访问控制、审计和主题。只有 *operate* 工具链需要等待 OAP 的 **admin host**(`:17128`),也就是随 **OAP
11.0,即将发布** 的 runtime rules、Live Debugger、Inspect、Cluster Status admin pane
和模板编辑发布。
+- **暗色优先,高密度。** 12 列网格面向 incident 扫描设计,首屏承载更多信号,减少不必要留白。
+- **基于现代技术栈。** 前端是 Vue 3 + TypeScript on Vite、Pinia、Apache ECharts、D3 和
Monaco;BFF 使用 Node.js 上的 Fastify。
+- **Apache 许可证,社区共建。** Horizon UI 位于
[apache/skywalking-horizon-ui](https://github.com/apache/skywalking-horizon-ui)。欢迎接到你的集群上试用,也欢迎告诉我们缺什么,issue
和 pull request 都可以。
+
+下一篇:仪表盘,以及为什么一个组件可以在服务端判断自己不适用于当前对象,并且连查询都不发。
diff --git a/content/zh/2026-06-22-horizon-ui-3d-infrastructure-map/index.md
b/content/zh/2026-06-22-horizon-ui-3d-infrastructure-map/index.md
new file mode 100644
index 00000000000..6c9321e3e33
--- /dev/null
+++ b/content/zh/2026-06-22-horizon-ui-3d-infrastructure-map/index.md
@@ -0,0 +1,91 @@
+---
+title: "认识 Horizon UI · 5/17:3D 基础设施地图"
+date: 2026-06-22
+author: 吴晟
+description: "Horizon UI 系列第五篇:用一个 WebGL 场景把整个部署放到同一张 3D 地图里,把各个 Layer
的服务渲染成立方体,并同时展示实时流量、告警和调用关系。"
+tags:
+ - Cloud Native
+ - Engineering
+---
+
+*译自英文原文:[Meet Horizon UI · 5/17: The 3D Infrastructure
Map](/blog/2026-06-22-horizon-ui-3d-infrastructure-map/)。*
+
+这是 [Meet Horizon UI](/zh/2026-06-21-skywalking-horizon-ui-introduction/)
系列的第五篇。[第三篇](/zh/2026-06-21-horizon-ui-topology-and-dependency/)画的是服务 *之间*
的地图,[第四篇](/zh/2026-06-21-horizon-ui-deployment-and-banyandb/)画的是单个服务 *内部*
的地图。这一篇把视角拉到最远:用一个 **WebGL** 视图一次看完整个部署。每个 SkyWalking Layer 的服务都会渲染成立方体,堆叠到 3D
空间里,并显示实时流量、告警和它们之间的调用关系。它补上了按 Layer 仪表盘之外的全局视角:退后一步,看整个系统。
+
+它也确实可以交互。所以这里直接放出来:下面就是运行在 demo 示例数据上的真实地图。拖动旋转,滚轮缩放,点击立方体:
+
+{{< map3d poster="/images/home/horizon-3d-map.png" badge="交互演示 · 示例数据" >}}
+
+## 一张 3D 图查看完整部署
+
+3D 地图是 `/3d/map` 里的独立全屏页面,从顶栏里的 **3D Infra**
入口打开。它刻意拿掉控制台其余部分:没有侧边栏,没有顶栏,没有全局时间选择器。整个 viewport 都交给场景。SkyWalking
标识放在左下角,右上角的 `×` 返回 Horizon。里面所有数据都来自 Horizon 其他页面访问的同一个 OAP:服务清单、每个 Layer
的拓扑、每个服务的流量,以及活跃告警。
+
+
+图 1:一张 3D 图看完整部署:服务是立方体,Layer 是带颜色的 zone,角色按 tier 堆叠。</br>
+
+## 用 tier 组织系统层次
+
+**Tier** 是一层横向平面,用来把系统中职责相近的 Layer 放在一起。Tier
从上到下的阅读顺序,就是请求流动的大致方向:从用户触达的应用,一路到底层平台。Horizon 内置四个 tier:
+
+- **Apps**(顶部):应用界面和应用视角看到的依赖,包括 General agent services、Browser/RUM、mobile,以及
`Virtual*` targets(database、cache、MQ、gateway、GenAI)。
+- **Middleware**:数据和消息服务、网关,以及自观测组件,包括
MySQL、PostgreSQL、Redis、Kafka、RocketMQ、APISIX、Nginx、SkyWalking SO11Y components
和云托管数据服务。
+- **Service Mesh**:承载应用流量的 mesh,包括 Istio control/data plane、Cilium、Envoy AI
Gateway。
+- **Infra**(底部):其余内容运行在其上的平台,包括 Kubernetes、hosts、VMs。
+
+OAP 上报的每个 Layer 都会归入一个 tier。Horizon 还没分类的新 Layer,比如 OAP 新增的 Layer,会归入
**failover tier**(默认 Middleware)并带 "unclassified"
标记。这样它会出现,运维人员也可以重新分配,而不是静默从地图上消失。右侧面板映射了整个堆栈:点击 tier
行可以把镜头移到对应位置,用眼睛开关一次显示或隐藏整个 tier(或单个 Layer),并查看当前可见服务数量。
+
+## 地图元素:立方体、zone 和流量
+
+每个服务对应一个 **立方体**。立方体会在 tier 上按所属 Layer 聚成一个 **zone**:半透明矩形使用该 Layer 的品牌色,并盖上项目
logo(Istio 的帆、Kubernetes 舵轮、数据库圆柱、队列图标),所以从任何视角都能辨认出 zone。带拓扑的
Layer(General、Service Mesh、Kubernetes Service、Cilium)会按 **调用依赖** 摆放立方体:上游
caller 在一侧,下游服务在另一侧,对应 2D 服务地图的 3D 版本。没有拓扑的 Layer 则把立方体排成整齐网格。
+
+立方体下方的小 **traffic** 标签显示该服务的实时主吞吐:应用和 mesh 服务是 requests per minute,数据服务是
queries 或 operations per
second,并保留各自单位。只有足够近、可读时标签才出现;缩远后会淡出,保持场景干净;选中的立方体总会显示它的数字。
+
+## 用 Beacon mode 突出告警服务
+
+当一个服务有 **当前正在触发的告警** 时(Horizon 轮询最近 20 分钟,并只统计 service 作用域下仍在触发的告警),它的立方体会
**红色脉冲**。这就是一个信标,即使隔着整个场景也能看到,而告警列表会独立刷新。
+
+在繁忙地图里,几百个立方体中的一个红点仍然可能难找,所以有 **Beacon mode**。从工具栏打开后,所有 *健康* 立方体都会变成深色
wireframe,只留下正在告警的服务发光。部署结构仍然清楚,但真正出问题的服务才有颜色。这个模式可以把鸟瞰图快速切成 incident triage 界面。
+
+
+图 2:Beacon mode 会把健康对象变暗成幽影,所以你只会看到正在触发的服务。</br>
+
+## 线表示调用和层级关系
+
+地图不只画节点,也画调用图:
+
+- **In-layer calls**:同一个 Layer 内两个服务之间的浅青色管线,并带沿线运动的数据包动画。这是每个 Layer
自己的内部调用图,始终开启。
+- **Cross-layer calls**:同一个 tier 上不同 Layer 服务之间的柔和琥珀色箭头,比如 Browser app 调用
Frontend、Frontend 调用 Virtual Database,方向从 caller 指向 callee。
+- **Hierarchy links**:这是让 3D 布局真正发挥价值的一类线。选中一个立方体后,粗灰色管线会连接 **同一个逻辑服务在不同 tier
上的多张面孔**:agent 看到的服务、mesh 看到的服务、Kubernetes service 看到的服务。它们表示
*身份*,不是流量,所以默认隐藏;选中立方体后只显示与它相关的对象,并沿着 tier
一层层爬上去。这就是[第三篇](/zh/2026-06-21-horizon-ui-topology-and-dependency/)里的
Smartscape,用它本来就该拥有的维度画出来。
+
+
+图 3:选中立方体后,身份链接沿 tier 爬升:同一个服务,被 agent、mesh 和 Kubernetes 分别看到。</br>
+
+## 视角移动与选择
+
+拖动旋转,滚轮缩放,**方向键** 或 **WASD** 平移视角(按住 **Shift**
步长更大);左上角工具栏也为触控板提供同样动作的按钮。有一条刻意设计的规则值得知道:**3D
场景里的点击永远不会移动视角**,只负责选择。点击一个立方体,它会高亮,旁边出现详情卡片(服务名称、tier、Layer,以及会在新标签页打开该服务
Layer 仪表盘的 **Open dashboard** 按钮),它的 hierarchy links 也会亮起。移动视角的交互入口是 *侧边面板* 和
*工具栏*;点击 Layer 行,镜头会滑到它的 zone。把“选择”和“移动视角”分开,点击小立方体才会可靠,不会刚点中它就让它从光标下滑走。
+
+## 地图如何分阶段加载
+
+整个部署的数据太大,不适合一次请求拉完,所以地图分阶段加载,底部细长的 **timeline strip**
会实时展示进度:**Services**(服务清单和所属 Layer)→ **Templates**(哪些 Layer 带拓扑)→
**Topologies**(每个有拓扑 Layer 的调用图)→ **Hierarchy**(跨 tier 身份链接)→
**Layout**(摆放立方体)→ **Metrics**(按批次获取每个服务的流量,让立方体逐步亮起来)。点击任意阶段可以打开抽屉查看详情,也可以点击
refresh 重新跑完整流程。
+
+两个设计让刷新变便宜。**Hierarchy**
阶段是增量的:只有上次之后新增的服务需要探测,其余从缓存复用,所以稳定部署在这一步没有额外代价。场景还会按每个 Layer 的结构 hash 更新
key;结构没变时刷新会保留你的视角位置,只有服务清单或边真的变化时才重建布局。底层使用 [Three.js](https://threejs.org/)
加一层很薄的 Vue wrapper,同类立方体共享 geometry 和 material。正是这些细节,让几百个服务也能在浏览器标签页里平滑渲染。
+
+## 地图结构来自配置
+
+上面这些不是一张写死的 "3D screen"。地图展示什么,由管理员在 `/admin/3d-map` 的 **结构化表单编辑器**
里编辑:tier、Layer、颜色和指标都通过表单控制,而不是直接写 JSON。你可以在里面:
+
+- **用一个全局正则过滤 Layer**:匹配排除的内容会完全从地图上消失。
+- **安排 tier**:重命名、从上到下重排,并把每个 Layer 固定到某个 tier 上,同时指定 failover tier,避免内容静默丢失。
+- **对 Layer 分组**:把多个相关 Layer,比如 SkyWalking 自观测组件,聚成一个带标签的 block,每个成员仍然保留自己的颜色。
+- **为每个 Layer 配色并选择流量指标**:配置 MQE 表达式、label 和单位;默认值会从该 Layer 的仪表盘模板里初始化,所以大多数
Layer 一开始就能显示合理数字。
+
+Horizon 自带一份默认配置,所以地图开箱就有用。你的修改会以本地 draft 保存,直到点击 **Check diff & push** 发布到
OAP。它使用和仪表盘相同的 draft → preview → publish 模型,也支持同样的
Export/Import,用于备份或在部署之间迁移配置。地图本身是只读 **observe** 界面,可以直接运行在当前 OAP
上;发布用于控制地图形态的配置,则属于后续文章会完整介绍的配置化定制。
+
+
+图 4:地图是配置,不是代码。tier、颜色和每个 Layer 的流量指标都以表单方式编辑,然后发布到 OAP。</br>
+
+## 后续阅读
+
+3D 地图是全局入口;2D 的按 Layer 页面仍然是最完整的服务地图。查看它只需要读权限(`infra-3d:read`,内置 viewer
角色及以上持有);调整它需要和仪表盘相同的写权限。字段参考,包括 tier、配置结构和加载阶段,可以看 [3D Infrastructure Map
文档](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/infra-3d-map/)。
+
+下一篇:**Trace Explorer**。从整个部署的鸟瞰图,回到单个请求,并用三种方式画出来。
diff --git a/content/zh/2026-06-22-horizon-ui-trace-explorer/index.md
b/content/zh/2026-06-22-horizon-ui-trace-explorer/index.md
new file mode 100644
index 00000000000..00a8c40b161
--- /dev/null
+++ b/content/zh/2026-06-22-horizon-ui-trace-explorer/index.md
@@ -0,0 +1,92 @@
+---
+title: "认识 Horizon UI · 6/17:Trace 探索器"
+date: 2026-06-22
+author: 吴晟
+description: "Horizon UI 系列第六篇:按 Layer 使用的分布式 Trace 探索器:先配置条件再查询,在时延分布图上框选异常
Trace,并用瀑布图、调用树和统计表阅读同一条 Trace。"
+tags:
+ - Tracing
+ - Cloud Native
+---
+
+*译自英文原文:[Meet Horizon UI · 6/17: The Trace
Explorer](/blog/2026-06-22-horizon-ui-trace-explorer/)。*
+
+这是 [Meet Horizon UI](/zh/2026-06-21-skywalking-horizon-ui-introduction/)
系列的第六篇。前几篇都在讲地图:服务之间的[拓扑](/zh/2026-06-21-horizon-ui-topology-and-dependency/)、单个服务内部的
[deployment](/zh/2026-06-21-horizon-ui-deployment-and-banyandb/),以及整个系统全貌的 [3D
view](/zh/2026-06-22-horizon-ui-3d-infrastructure-map/)。它们回答“我的系统长什么样”。这一篇把视角缩到
*一个请求*,看它的 spans、时序,以及到底是哪一跳变慢了。这就是 **Traces** 标签页。
+
+## 面向排查,不做实时滚动刷新
+
+Traces 标签页是一个位于 **Layer 内部** 的分布式 Trace 探索器:选择服务,设置条件,然后阅读单条 Trace 的 span
时间线。它有意和控制台其他部分不一样,因为 Trace 是排查数据,不是实时流。
+
+它使用 **独立的时间范围和条件**。它不跟随全局顶栏时间选择器,也不会自动刷新。你先配置要找的条件,再按 **Run
query**;按之前不会取任何数据。第一次运行前,列表只显示 *"Pick your conditions, then click Run
query."*。当你在追二十分钟前的一条坏 Trace 时,最不需要的就是窗口每几秒自动往前滚,所以它不会这么做。
+
+## 用表单配置条件,不写查询语言
+
+过滤条件全部通过结构化表单配置:select、数字范围、tag 标签。条件会暂存在工具栏里,只在点击 **Run query** 后生效:
+
+- **Instance** 和 **Endpoint**:在服务内部收窄范围,endpoint 下拉框只列出这个服务自己的 endpoints。
+- **Status**:`ALL` / `SUCCESS` / `ERROR`。
+- **Order**:Newest(按开始时间)或 Slowest(按耗时)。
+- **Limit**:拉取多少行,默认 30;BFF 会在服务端限制 page size,避免客户端向 OAP 请求过量数据。
+- **Time range**:滚动预设(最近 15 分钟到 24 小时)或自定义绝对窗口,按 **秒级精度** 计算,所以刚结束的 Trace
不会因为分钟取整而掉出窗口。
+- **Trace ID**:粘贴一个 id 直接查。
+- **Duration range**:毫秒级 min-max。
+- **Tag**:自由输入 span tags,格式为 `key=value`(比如 `http.status_code=500`),按 Enter
添加为可删除标签,多个 tag 按 AND 连接;key 和 value 都从后端获得 typeahead。
+
+它不是查询语言。Horizon 里没有 TraceQL 输入框。上面的结构化条件就是完整界面。(TraceQL 是另一条路径:SkyWalking
后端可以通过 TraceQL 向 Grafana 提供
Trace,这件事在[另一篇文章](/zh/2026-04-08-traceql/)里讲。Horizon 的探索器是表单,不是 DSL。)
+
+## 可框选的时延分布图
+
+结果返回后,工具栏下会出现一张 **Distribution** 图:每个点代表一条 Trace,X 轴是开始时间,高度是 duration,越慢的
Trace 越高。点按 **status** 着色:错误为红色,成功为强调色。所以左上角、右上角那类高处红点,就是你要找的“又慢又失败”的区域。
+
+这张图本身也是过滤器。点击一个点可以选中它,或者 **拖一个矩形框选一片点**,结果列表会收窄到这批选择;标题区切换成 *"N picked"*
计数,并提供 Reset。这是基于已加载结果的客户端过滤,不会发起新查询。直接框住慢且失败的角落,只读那几行,是从“200 条 Trace”缩到“这 6
条值得打开”的最快路径。
+
+
+图 1:先配置条件、执行查询,再在分布图上框选一片区域,把列表缩到真正值得打开的 Trace。</br>
+
+每条结果行会显示 Trace 的 root endpoint、OK/ERR 标记、duration,以及按当前结果中最慢 Trace
归一化后的长度条。每一行 *代表什么* 取决于存储后端,Horizon 会自动检测:横幅会显示 *"Full traces are returned
inline"*,表示后端返回的是带 spans 的完整 Trace,点击即可打开;或者显示 *"Each row is a trace segment —
click one to fetch its full trace."*。你不需要配置这个,横幅只是告诉你当前看到的是什么。
+
+## 三种方式阅读同一条 Trace
+
+点击一行后,Trace 会打开,并在同一组 spans 上提供三种视图切换:**Default**、**Tree** 和 **Statistics**。
+
+- **Default** 是 span 瀑布图:每个 span 一行缩进展示,带一个按服务着色的条形,条形在共享时间线上按 span
起始偏移和耗时定位;同时显示 span-kind glyph、组件图标(和拓扑地图共用同一套图标)、endpoint 或 peer 名称,以及 span
自身耗时。错误 span 会高亮,带 attached events 的 span 会有标记。关键点在于,瀑布图会用 parent references
**把跨 segment 的 spans 串起来**,所以一个跨过五个服务的请求会渲染成一条连贯时间线,而不是五段互不相干的内容。
+- **Tree** 把同一批 spans 画成可缩放、可平移的节点图:root 在左,callees
向右流动。适合你更关心调用树形状,而不是精确时序的时候。
+- **Statistics** 按 **name** 聚合 spans:一张可排序表,展示每个 operation 的 count、total /
average / maximum duration。所以“这条 Trace 里到底哪个 span 名称累计耗时最多”只需要点一次排序。
+
+
+图 2:瀑布图(Default),一条请求触达的所有服务被画成同一条连贯时间线。</br>
+
+
+图 3:Tree 视图,把同一批 spans 画成调用树形状,可以缩放和平移。</br>
+
+## 查看 span 详情
+
+点击任意 span,旁边会打开详情面板。**Meta** 展示核心信息:service、instance、endpoint、kind(entry /
exit / local / producer / consumer)、component、peer、layer、start time、duration 和
error 标记。适用时,下面还会出现:
+
+- **Cross-trace refs**:当一个 span 的 parent 位于 *另一条* Trace(异步跳转、稍后消费的消息)中时,这里会列出
parent 的 trace id、segment 和 span;trace id 是一个 **链接**,点击可以直接切换到那条 Trace。
+- **Tags**、**Logs**(每个 span 的带时间戳日志项)和 **Attached Events**(带 start/end time 和
summary key/values 的命名事件)。
+
+详情标题区会显示 Trace 开始时间、总耗时、span 数量,以及触达了多少个不同服务;从这里可以复制 trace id 或可分享链接。打开一个带
`?traceId=...` 的分享 URL,会直接打开这条 Trace 的 overlay。这让 Trace 成为可以粘贴到 incident
channel 里、让同事打开同一视图的对象。
+
+## Native 与 Zipkin 并列支持
+
+并不是每个 Layer 的 Trace 都来自 SkyWalking 自己的 agent。Layer 模板带一个 `traces.source`
设置,可以是 `native`、`zipkin` 或 `both`,Horizon 据此路由。由 agent 接入的 Layer(比如 General
Service)使用上面讲的 **native** 探索器;service-mesh 和 Kubernetes 风格的 Layer 中,spans 以
Zipkin/OpenTelemetry 数据进入,则使用 **Zipkin** 探索器;设置为 `both` 的 Layer 会得到
**两个标签页**,因为 native 和 Zipkin spans 的形状和条件确实不同。
+
+Zipkin 标签页通过 **OAP 的 Zipkin query API** 查询上游 Zipkin store。这是 OAP 向任何 Zipkin
client 暴露的兼容接口,不是 GraphQL,也不是 TraceQL。Zipkin 按自己的服务集合组织数据(每个 span 上的
`serviceName`,可能和 SkyWalking 的服务列表不同),所以这个标签页有自己的服务控件,不绑定页面上的 service
picker;它也带 Zipkin 原生条件,比如 *Remote service*、*Span name* 和 *Annotations*
查询(`error` 或 `key=value`)。两个存储路径独立失败:Zipkin 不可达时,native traces 不受影响,反之亦然。
+
+
+图 4:设置为 Zipkin 的 Layer 会得到自己的标签页和自己的服务集合,并通过 OAP 查询上游 Zipkin store。</br>
+
+打开一个 Zipkin span 后,详情会保留 Zipkin 的形状:`CLIENT` / `SERVER` kind、本地和远端
endpoint,以及原始 Zipkin/OpenTelemetry tags(`istio.*`、`http.status_code`、sidecar
`node_id`)都会按 Zipkin 记录的方式展示,不翻译成 SkyWalking span 模型。
+
+
+图 5:Zipkin span 保留自己的字段:kind、endpoints,以及原始 `istio.*` / HTTP tags。</br>
+
+## 从慢记录定位到对应 Trace
+
+还有一种进入 Trace 的方式,不需要经过探索器。整个应用只挂载一个 Trace overlay,多个入口都能按 **trace id**
打开它:`?traceId=` 链接、cross-trace
ref、日志行,以及[第二篇](/zh/2026-06-21-horizon-ui-dashboards-and-mqe/)里提到的 record
组件慢语句行上的 **jump-to-trace** 图标。因为它按 id 解析,而不是按 Layer 解析,所以即使 Trace 属于你当前看到的
*另一个* Layer 也能工作:Virtual Database、Cache 或 MQ 服务没有自己的 traces 标签页,但它的慢语句仍然能带你到发起方
Trace。跳转如果带 timestamp(日志行知道自己写入时间),查找会围绕那个时间放宽窗口,所以即便 Trace 已经进入冷存储,也不会悄悄找不到。
+
+## 后续阅读
+
+Traces 标签页把一个请求完整展开;前面几篇的仪表盘和地图,是你最初发现异常的地方。字段参考,包括每个条件、native-vs-Zipkin 拆分和
span 详情面板,可以看 [Traces
文档](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/traces/)。
+
+下一篇:**Log Explorer**。同样的排查思路,应用到日志流而不是 spans。
diff --git
a/content/zh/2026-06-23-horizon-ui-browser-errors-and-source-maps/index.md
b/content/zh/2026-06-23-horizon-ui-browser-errors-and-source-maps/index.md
new file mode 100644
index 00000000000..3d876173634
--- /dev/null
+++ b/content/zh/2026-06-23-horizon-ui-browser-errors-and-source-maps/index.md
@@ -0,0 +1,69 @@
+---
+title: "认识 Horizon UI · 8/17:浏览器错误与 Source Map"
+date: 2026-06-23
+author: 吴晟
+description: "Horizon UI 系列第八篇:浏览器端 Agent 上报的 JavaScript 错误流,以及如何借助 source map
把生产环境混淆栈逐帧还原到原始文件、行、列、符号和源码片段。"
+tags:
+ - Logging
+ - Engineering
+---
+
+*译自英文原文:[Meet Horizon UI · 8/17: Browser Errors & Source
Maps](/blog/2026-06-23-horizon-ui-browser-errors-and-source-maps/)。*
+
+这是 [Meet Horizon UI](/zh/2026-06-21-skywalking-horizon-ui-introduction/)
系列的第八篇。[第七篇](/zh/2026-06-23-horizon-ui-log-explorer/)讲的是服务日志;这一篇讲 *用户*
遇到的错误,也就是浏览器端 agent 上报的 JavaScript 异常,以及把这些错误定位到源码的关键一步。
+
+生产环境 JavaScript stack 基本不可读。代码经过压缩和打包后发布,浏览器只会报告错误出现在
`app.min.js:1:98412`,也就是一段机器生成代码里的位置,几乎不给你任何线索。Horizon 要做的是借助正确的 **source map**
把这条 stack 还原回你的源码:原始文件、行、列、符号名,以及出错位置附近的代码片段,逐帧还原。
+
+## 浏览器端错误流
+
+在 **BROWSER** Layer 上,**Browser Logs** 标签页(屏幕上的标签名,它专门表示 JavaScript 错误流)会列出
browser agent 上报的内容。BROWSER Layer 会把槽位重命名成自己的语义:services 变成
**Applications**,instances 变成 **Versions**,endpoints 变成 **Pages**。这个列表的阅读方式类似
[Log Explorer](/zh/2026-06-23-horizon-ui-log-explorer/):可点击的 **category**
legend 带计数,density histogram 位于日志流之上。每一行都有时间、**category**、page、app version
和错误消息;如果带有 minified `line:col`,也会显示成标记。
+
+排查方式和 trace/log 标签页一致:它使用独立的 **Time range**(全局顶栏暂停),你可以按 **Version**、**Page**
或 **Category** 收窄,然后点击 **Run
query**。没有后台轮询把视图不断往前推,也没有要学习的查询语言,只有结构化控件。点击一行后,它会在日志流里原地展开。
+
+
+图 1:browser agent 上报的错误流:按 category 组织、带图表,并限定到某个 app 版本和页面。</br>
+
+那个 minified `line:col` 就是最典型的问题。它是真实位置,但位置在 *构建后* 的 bundle
里,不在你的源码里。后面的解析流程就是为了解决它。
+
+## 从混淆后的 stack 定位到源码
+
+展开一个错误后,面板分成两侧:左边是浏览器原样报告的 **raw stack**,也就是那段很难读的生产栈;右边是解析结果区域。选择一个 **source
map**,点击 **Resolve**,Horizon 会解析 stack,并通过这份 map 映射 **每一帧**:
+
+- 每帧原始 **`file:line:column`**;
+- 原始 **symbol name**(如果 map 携带了);
+- 出错行附近几行 **原始源码**,命中行会高亮(如果 map 内嵌 `sourcesContent`)。
+
+map 覆盖不到的 frame 会明确标为 `unmapped`。所以一条顶层 frame 为 `app.min.js:1:45` 的
stack,可以还原成 `checkout.ts:2:20` 上的 `computeCartTotal`,并把 `checkout.ts`
附近几行显示出来。真正抛错的 `cart.items.reduce(...)` 就在面板里,不只还原第一帧,而是从上到下还原整条 stack。
+
+这里有一些细节决定结果是否可信:浏览器 stack 的列号从 1 开始计数,source map 从 0
开始计数,所以解析器每次查找前都会做偏移;这条路径用真实 bundler 输出测试,而不是手写 fixture。
+
+
+图 2:核心流程:把 minified stack 指向正确 map,然后逐帧还原到你自己的源码。</br>
+
+## 哪些错误可以用 source map 解析
+
+不是每类错误都有可还原内容。**`JS`**、**`PROMISE`** 和 **`VUE`** 是真实 JavaScript 错误,它们的 stack
指向 bundle,可以解析。**`AJAX`** 和 **`RESOURCE`** 是网络和加载失败;它们的“stack”是 HTTP status 或失败
URL,不是代码,所以 source map 没有东西可映射(Horizon 不会阻止它们,只是没有可映射的 JavaScript 位置)。没有 source
map 的代码、`eval` 或 inline scripts 里的 frame,也会保持 `unmapped`。(`JS` 也是唯一由浏览器上报顶层
`line:col` 的 category;其他 category 的位置在 stack 字符串内部,由解析器提取。)
+
+## 提供 source map:上传或挂载
+
+解析前必须让 map 可用。Horizon 提供两种方式,耐久性刻意不同:
+
+- **Upload** 一个 `.map`,直接从标签页上传。它只保存在服务端 **内存**
里,没有后端存储,并且临时性是设计目标:它占用内存预算,在压力下按 least-recently-used
淘汰,**服务重启后丢失**;多实例部署时,它也只存在于接收上传的那一个实例。这个路径适合临时排查:拖一个 map 进来,解析,处理完离开。
+- **Mount** `.map` 文件到服务端 **source-map 目录**(容器镜像中是 `/app/sourcemaps`,可通过
`HORIZON_SOURCEMAPS_DIR` 指定)。这些文件在启动时按 Source Map v3
校验,按需从磁盘读取(所以不占内存预算),重启后仍然存在,会自动 reload,并且 **不能从 UI 删除**。这是生产路径:把构建产物里的 maps
放进镜像或挂载目录,它们就一直可用。
+
+管理器会显示每个 map 的来源(*uploaded · temporary* 还是 *mounted ·
durable*),以及当前内存预算使用量。预算配置,包括单文件上限和常驻上传总量上限,默认 64 MiB 和 512 MiB,位于
`horizon.yaml` 的 `sourceMaps` 块。
+
+
+图 3:两种提供 map 的方式:upload 用于快速排查,mount 用于生产环境长期使用。</br>
+
+## map 必须手动选择
+
+Horizon 刻意 **不猜测**。browser agent 会上报 app **version**,但不会上报精确 build
fingerprint,所以没有安全方法自动把一个错误匹配到某份 map。用错 build 的 map
会给出非常自信、但完全错误的行号,比没有答案更糟。所以这里由你选择:挑选和该错误 build 对应的 map,并按版本给 map
清晰命名。(还有一个必须直说的注意点:source map 的 `sourcesContent` 会包含你的原始源码,所以无论上传还是挂载,都要把 map
当成敏感内容,只放在可信服务器上。)
+
+手动选择 map 这件事,也划清了 **权限** 边界。查看错误、列出 maps、**解析** stack 都是读操作,由
`browser-errors:read` 控制;**上传或删除** map 是写操作,由 `source-map:write`
控制。所以只读用户可以整天反混淆 stack,但没有权限改变已加载的 map 集合。读就是读,修改 map 存储才是写。
+
+## 后续阅读
+
+字段参考,包括 categories、两种提供路径、预算,以及如何按 build 匹配 map,可以看 [Browser Logs & Source
Maps
文档](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/browser-source-maps/)。
+
+下一篇英文系列将进入 **Profiling**:五种 profiler(trace、async、eBPF、Go
pprof、network)通过同一个火焰图呈现。
diff --git a/content/zh/2026-06-23-horizon-ui-log-explorer/index.md
b/content/zh/2026-06-23-horizon-ui-log-explorer/index.md
new file mode 100644
index 00000000000..f7880c656fd
--- /dev/null
+++ b/content/zh/2026-06-23-horizon-ui-log-explorer/index.md
@@ -0,0 +1,67 @@
+---
+title: "认识 Horizon UI · 7/17:日志探索器"
+date: 2026-06-23
+author: 吴晟
+description: "Horizon UI 系列第七篇:两类日志视图,一种查看已采集、已索引、可关联 Trace
的存储日志流,并提供按级别堆叠的直方图;另一种按需实时查看 Kubernetes pod 的容器输出。"
+tags:
+ - Logging
+ - Cloud Native
+---
+
+*译自英文原文:[Meet Horizon UI · 7/17: The Log
Explorer](/blog/2026-06-23-horizon-ui-log-explorer/)。*
+
+这是 [Meet Horizon UI](/zh/2026-06-21-skywalking-horizon-ui-introduction/)
系列的第七篇。[第六篇](/zh/2026-06-22-horizon-ui-trace-explorer/)讲的是一个请求的
spans;这一篇讲它周围的日志行。Horizon 用 **两个标签页** 展示日志,对应两类排查问题:*“这个服务过去半小时打了什么日志?”* 以及
*“这个 pod 现在正在向 stdout 打什么?”*
+
+- **Logs** 标签页查询 SkyWalking 已经 **采集并存储** 的日志:已索引、可过滤、可与 Trace 关联。
+- **Pod Logs** 标签页按需 **实时 tail** Kubernetes pod 的容器日志。这类日志不走 SkyWalking
的日志存储:OAP 直接从 **Kubernetes API server** 读取它们(也就是 `kubectl logs` 那条路径),Horizon
只展示当前窗口,然后丢弃,不会持久化。
+
+某个 Layer 展示哪些标签页由模板决定:启用日志的 Layer(General、Mesh、Nginx、Envoy AI Gateway、mobile 和
mini-program Layer)会显示 **Logs** 标签页;只有感知 Kubernetes 的 Layer(Kubernetes
Service、Mesh、Mesh data plane)会显示 **Pod Logs** 标签页。Browser JavaScript
错误又是另一类数据:它不是服务日志,而是浏览器端 agent 上报的客户端错误事件,有自己的分类,也有自己的 **source-map
反混淆**,可以把混淆后的 `app.min.js:1:...` frame 还原到原始 `file:line`。这是 Browser Layer
上的独立标签页,会在这个系列的另一篇文章里讲。
+
+## 查询已存储日志
+
+打开一个有 **Logs** 标签页的 Layer,先在顶部选择服务,最新日志流会按 newest-first 加载。和 Trace
探索器一样,这个标签页使用 **独立的时间范围**。当你在这里排查时,全局顶栏时间选择器会暂停,自动刷新不会把你正在看的窗口不断往前推。可以选择滚动预设(最近
15 分钟到 24 小时,默认 30 分钟),也可以选择自定义绝对窗口;查询按 **秒级精度** 执行,所以最新日志不会被分钟取整吞掉。
+
+条件栏用来收窄日志流,每个过滤条件都是可选的,多个条件按 AND 连接:
+
+- **Instance**:限制到某个服务实例。在 sidecar Layer 上标签显示为 **Sidecar**。
+- **Endpoint**:输入搜索服务 endpoints,点击固定,按 **×** 清除。
+- **Trace ID**:只显示和某条 Trace 关联的日志行。从 Trace 跳转过来时,也会预填这个字段并直接限定日志流。
+- **Tags**:单个 `key=value` 字段,带 autocomplete;输入 key 可看建议,输入 `=` 后切换到已知 value,按
Enter 提交。提交后的 tags 以可删除标签形式保留。
+- **Level**:日志流上方的 **Levels** 条也可以当过滤器用。点击 `error`、`warn`、`info` 或 `debug`
只保留该级别,再点一次清除。
+
+这里不提供单独的日志查询语言,也不需要学习 LogQL。上面的条件就是完整界面,并且 **编辑时会立即刷新日志流**;**Run query**
只是显式告诉系统“我改完了,现在刷新”,同时回到第一页。
+
+## 用直方图和级别计数定位日志
+
+日志视图的目标不只是 *列出* 行,还要帮你看出分布和异常,所以日志流上方有两个定位信息。
+
+**Density histogram** 按时间展示日志量,每个柱按 legend 颜色 **堆叠 level**;hover 柱子可以看到该
bucket 的时间范围和每个 level 的计数。它基于当前页面上可见数据绘制,所以展示的是你正在看的日志分布。**Levels** 条则保留每个
level 在窗口内的累计计数。这个计数跨整个查询窗口采样,而不只是当前可见页,所以 error/warn/info 比例反映的是整个窗口。
+
+日志行会展示 timestamp、level(行颜色跟随 level)、service、存在 Trace 关联时的 **↗ trace**
链接、**`JSON` / `YAML` / `TEXT` 格式标记**,以及内容的一行预览。Horizon 按日志内容本身决定这个标记:OAP 会标注日志
body 是 JSON 还是 plain text,在此基础上 Horizon 还会嗅探 JSON 和 YAML
结构,所以即使一行没有被标注但内容是结构化的,也会得到正确处理。JSON 在预览里压平成一行,YAML 保留 key,plain text 会折叠空白。
+
+
+图 1:某个服务的存储日志流:窗口上的 level histogram 和 level 计数,下面是每行日志,带 JSON / YAML / TEXT
标记并可跳到 Trace。</br>
+
+## 查看单行日志详情
+
+点击一行后,完整日志内容会在弹层里打开:内容会按格式进行 **pretty-printing**,JSON 和 YAML 正常排版,plain text
则获得完整画布,而不是被挤在一条窄条里。面板还提供 **Copy** 按钮、service / instance / endpoint / trace
上下文,以及该行所有 tag 的表格。日志行与 Trace 关联时,**↗ trace** 按钮会在 overlay 中打开相关 [Trace
瀑布图](/zh/2026-06-22-horizon-ui-trace-explorer/),不离开日志流;它还会把这行日志的 timestamp
传过去,所以 Trace 即使已经进入更冷的存储 tier,也仍然能找到。按 Escape 或点击 backdrop 即可关闭。
+
+
+图 2:完整查看一行日志:内容按格式排版,旁边展示上下文和所有 tags。</br>
+
+## Pod Logs:实时查看容器输出
+
+**Pod Logs** 标签页回答另一个问题,它的数据源也完全不同:不是 SkyWalking 存储的日志,而是通过 OAP 从 **Kubernetes
API server** 实时读取 pod 的容器输出,也就是 `kubectl logs -f`
读取的同一类内容。这里没有可翻页的存储历史;每次刷新拉取尾部窗口,展示出来,然后丢弃。
+
+启动 tail 只需要几个选择:选一个 **Pod**(固定的服务实例)、一个 **Container**(Horizon 会列出 pod 的
containers 并默认选第一个)、一个回看 **Window**(last 30s、1m、5m、15m 或 30m,决定每次轮询向前取多远),以及轮询
**Interval**(2s、5s、10s 或 30s,决定多久重新取一次)。按 **Start**
后,日志会流入只读查看器,保持最新行可见,并持续轮询直到你按 **Pause**。顶部状态条显示 container、行数、live
indicator,以及上次更新距今多久。两行 **Include** / **Exclude** filter 用来收窄可见内容;每个标签都是一个
**整行正则表达式**(`.*error.*`),由 OAP 执行,所以它匹配整行而不是子串,并且可以叠加。
+
+使用前需要知道一点:按需读取 pod logs 在 OAP 上 **默认关闭**,因为容器输出可能包含 secret。功能关闭时,或者你选择的 pod
已经滚动或缩容消失时,OAP 会返回一个 *原因*,Horizon 会把它显示成横幅,而不是给你一个空面板。这样你能区分“需要打开这个功能”和“那个 pod
已经不存在”。
+
+
+图 3:一个 pod 容器的实时 tail:窗口化、按间隔轮询、可用正则过滤、永不持久化。</br>
+
+## 后续阅读
+
+两个标签页,包括存储查询、tag 和 container autocomplete、live tail,都由同一个 `logs:read`
权限控制。所以授予“可以读日志”就是一个开关。字段参考,包括每个条件、histogram、Pod Logs 的窗口和过滤器,可以看 [Logs
文档](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/logs/)。
+
+下一篇:**Browser & RUM monitoring**。browser agent 自己的错误流,以及如何用 source map 反混淆
minified stack。