You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2026/06/28 16:46:42 UTC
(skywalking-website) branch blog/horizon-ui-cn-translations created (now a58ec325fd6)
This is an automated email from the ASF dual-hosted git repository.
wu-sheng pushed a change to branch blog/horizon-ui-cn-translations
in repository https://gitbox.apache.org/repos/asf/skywalking-website.git
at a58ec325fd6 docs(blog): add Chinese Horizon UI translations
This branch includes the following new commits:
new a58ec325fd6 docs(blog): add Chinese Horizon UI translations
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(skywalking-website) 01/01: docs(blog): add Chinese Horizon UI translations
Posted by wu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
wu-sheng pushed a commit to branch blog/horizon-ui-cn-translations
in repository https://gitbox.apache.org/repos/asf/skywalking-website.git
commit a58ec325fd64633f0293e9975072880c9ae15911
Author: Wu Sheng <wu...@foxmail.com>
AuthorDate: Mon Jun 29 00:46:14 2026 +0800
docs(blog): add Chinese Horizon UI translations
---
.../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..c5937f6a30d
--- /dev/null
+++ b/content/zh/2026-06-21-horizon-ui-dashboards-and-mqe/index.md
@@ -0,0 +1,91 @@
+---
+title: "认识 Horizon UI · 2/17:会适配实体的仪表盘"
+date: 2026-06-21
+author: 吴晟
+description: "Horizon UI 系列第二篇:它的仪表盘如何由 MQE 表达式驱动,如何隐藏不适用于当前实体的组件并在服务端跳过查询,以及如何把 1 显示成 OK、把 4.51e4 显示成 45.1k。"
+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 表达式**。它值得单独写一篇,不只是因为能展示数字,而是因为它围绕数字做了很多实际工作:不适用于当前实体的组件会被隐藏,相关查询也完全不会发出;编码值和原始字节数会被渲染成人能读懂的形式;页面上所有图表都绑定到同一个游标;你还可以从一条慢 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,合适的组件会自己渲染。而且每个层级都使用同一套网格系统。Layer 模板里的 `dashboards.<scope>` map 会为 **service**、**instance** 和 **endpoint** 页面携带不同的组件集合,所以向下钻取时,整个仪表盘会换成正确的 scope。(这些都运行在[第一篇](/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 scope 上,根据选中实例的属性开关,比如 `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`,并继续驱动顶部 header。每个固定实体都有自己的颜色。之后每个组件都会内联对比:line 组件为每个实体叠加一条序列,card 为每个实体显示一行,`top` 和 `record` 组件增加按实体切换的标签页,table 增加 Entity 列。持久的对比栏会保持这个实体组,不受底层列表分页或当前查看实体变化影响;每个实体单独发起请求,所以一个实体慢,不会把其他实体拖成空白。
+
+
+图 7:锁定实体,甚至跨服务锁定,所有折线组件都会按颜色叠加;对比栏保持实体组,CURRENT 实体仍然驱动 header。</br>
+
+## 时间选择器驱动整个仪表盘
+
+顶部时间范围会驱动页面上的所有内容:header KPI 条、组件主体,以及 BanyanDB 分层 hot/warm/cold 存储里的 **Cold** pill 完整流程。过去落地页和拓扑路由固定在最近 60 分钟,所以选择 "12 days ago" 后仍然会悄悄展示近期数字;现在时间选择器在所有地方都生效。上游控制变化时,每个依赖它的格子会明确重置并显示 "Reading data..." 提示,而不是在 spinner 下面留着旧值。
+
+## 下一步去哪里
+
+需要强调的是,这是一套系统。同样的五类组件、同样的 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` 条件、格式,以及每个 scope 的网格,都可以从 **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..5c89f253fe9
--- /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 节点、边流动动画和每条调用的指标侧栏都会复用,只是 scope 收缩到单个服务内部。
+
+它不是一团扁平节点云,关键在三点:
+
+- **实例渲染成六边形,并组合成 pod。** 一个 pod 的 **main** container 是完整六边形,**sibling** containers 会作为更小的六边形贴在它边上。所以主进程和 sidecar 会读成一个单元,跨 pod 的 sidecar 链接也会连到它真正所属的小六边形上。
+- **Pod 按你选择的规则聚到带标签的 box 里。** 规则可以是单个实例属性(role)、多个属性组合(比如 `node_role` + `node_type`),也可以是名称正则。这样一组混合角色节点会读成每个角色一个 box,而不是一团云。
+- **布局是分层的。** 每个 cluster box 会按调用深度摆放 pod:source 在左,被调用对象在右,所以 upstream→downstream 链条可以从左到右读;拖动任意 pod 后,它所属的 box 会重新流式布局,保持内容都被包住。
+
+边按 **(source-role → target-role)** pair 建 key,所以每类链接展示自己的指标,而不是共享一组扁平指标。主指标会直接印在边上,完整指标则进入 **Flows** 子标签页,每种 role-pair 对应一张对齐表。它默认关闭,和服务地图一样,完全从 **Layer dashboards admin → Deployment scope** 配置。
+
+## 像观测其他系统一样观测 BanyanDB
+
+这套机制是为一个明确场景准备的。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
+
+每个 scope 都有专门设计的仪表盘:
+
+- **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 scope,一眼看完整个数据库,并按角色列出所有容器。</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** scope 则给每个 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 scope,一次查看一个 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` scope;在那之前,Deployment 标签页仍然会画完整 inventory,只是 pod 之间没有边。
+
+## 配置出来的,不是写死的
+
+上面这些不是一张手工写死的 "BanyanDB screen"。聚类规则、每个 role 的节点指标、role-pair 边指标,都在 Layer template 中作为一个自包含块存在,可以从 **Layer dashboards admin → Deployment scope** 编辑,并随模板 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..f82fdfac4d8
--- /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 系列第三篇:一套由模板驱动、可为每个 Layer 重绘的拓扑引擎,降噪过滤器,从调用下钻到实例,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 叠加视图把一个服务的多张面孔连起来。它们共用同一套引擎,只是回答的问题不同。(Deployment 标签页,也就是单个集群服务 *内部* 实例的地图,以及 WebGL 3D 地图,都足够大,会在接下来的文章里单独讲。)
+
+## 一套拓扑引擎,按 Layer 重绘
+
+打开任意 Layer 的 **Topology** 标签页,你会看到一张从左到右排列的层次化服务地图:`User` 存在时位于最左侧,每个服务按调用深度落在不同列里;同一列内部则保留图遍历到它们时的顺序,所以主调用链可以自上而下读。每个服务都是一个 **六边形节点**,节点上展示的所有东西都来自这个 Layer 的配置,没有写死逻辑:
+
+- 六边形 **边框** 承载节点的 **ring** 指标,用类似 SLA 的健康色带展示(绿色 → 红色);
+- 组件 **图标** 放在六边形内部,和 Trace 瀑布图使用同一套图标,所以 PostgreSQL 节点像 PostgreSQL,Kafka 节点像 Kafka;
+- 节点头部数字,也就是 **center** 指标,带单位显示在六边形 **上方**;
+- 服务 **名称** 显示在节点 **下方**,**secondary** 指标(默认是时延)显示在名称下面;
+- 每条 **边** 都带调用吞吐的 **RPM chip**,使用服务端指标,缺失时回退到客户端指标。
+
+这里需要强调一点:上面这些都只是 **General Layer 的内置默认配置**。节点的 center / ring / secondary 指标,以及边指标,都位于 **Layer dashboards admin → Topology scope**,本质上是带单位和角色的 MQE 表达式。所以你可以把任意槽位指向另一条指标,同一套引擎就会为另一个 Layer 画出不同地图,或者按你的方式重画当前 Layer。(这些选择会像其他模板内容一样,跟着 Layer template 的 export/import 走。)
+
+
+图 1:一套由模板驱动的拓扑引擎:带健康色带的六边形节点、带 RPM chip 的边、真实组件图标;图上的每个指标都按 Layer 配置。</br>
+
+## 削掉噪声
+
+真实拓扑通常很吵。一张密集地图里会混入 OAP 没法完整识别的推测节点,比如裸露的 `rcmd:80`、未接入探针的地址。Horizon 的 **Filter** 控件可以关掉这些噪声,同时保留真实依赖。它会自动推导一个 facet:**按 Layer 分组**,展示方式和侧边栏一致。每一行都有 Layer 自己的图标和本地化名称,比如 *Virtual Database*、*Java Agent*,还会有一个 **Others** 桶,用来收纳 OAP 无法分类的节点,以及一个独立的 **User** 开关。取消勾选 *Others* 后,未接入探针的杂点和悬空边会消失,而数据库、缓存和队列(各自的 `VIRTUAL_*` 行)仍然留在图上。过滤在客户端执行,行会在每次刷新时重新推导,所以不会陈旧。
+
+
+图 2:一键降噪,去掉无法解析的 "Others" 节点,同时保留真实依赖。</br>
+
+当某个 Layer 的服务落在 OAP service group 里,地图上的 service-focus 选择器也会按这些 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 peers,节点上会贴一个小的 **chevron-stack chip**。
+
+
+图 6:选中节点上的 chevron-stack chip。它在选择时惰性探测,只在服务存在跨 Layer peers 时显示。</br>
+
+点击这个 chip,拓扑会在 **Smartscape** 叠加视图下变暗:焦点节点在原位高亮重绘,它的 peers 按 OAP 的 Layer 顺序纵向展开,请求近端 Layer 在上,基础设施近端 Layer 在下。之后两步点击就能在对应 Layer 中打开任意 peer,并完成预选。(叠加视图打开时自动刷新会暂停,避免内容在你眼前移动。)
+
+
+图 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..9b1ae491c5e
--- /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 后端重新设计的新一代 Web UI,从一条能实时反映系统全貌的侧边栏开始,让观测、运维、治理和定制都回到同一个控制台里。"
+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 开始上报,它就出现;安静下来,它就消失。菜单不会和现实漂移,因为菜单本身就是轮询出来的现实。
+
+
+图 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 名称和深命名空间也很常见,所以 shell 会主动让出空间:拖动分隔线可以调整侧边栏宽度,双击重置;也可以折叠成一条窄图标栏,把水平空间尽量交给画布。宽度会按浏览器记住。
+
+
+图 4:拖动分隔线加宽侧边栏,长名称不再被截断,宽度会按浏览器记住。</br>
+
+
+图 5:需要最大画布空间时,可以把侧边栏折叠成窄图标栏。</br>
+
+## 新增一层,后面的能力才成立
+
+过去,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 和结构化 payload。
+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..6ef2be5c2ab
--- /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 视图查看整个部署,把每个 Layer 的服务渲染成立方体并堆叠到不同 tier 上,同时显示实时流量、告警信标和调用关系。"
+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/map` 下的独立全屏视图,从顶栏里的 **3D Infra** pill 打开。它刻意拿掉控制台其余部分:没有侧边栏,没有顶栏,没有全局时间选择器。整个 viewport 都交给场景。SkyWalking 标识放在左下角,右上角的 `×` 返回 Horizon。里面所有数据都来自 Horizon 其他页面访问的同一个 OAP:服务清单、每个 Layer 的拓扑、每个服务的流量,以及活跃告警。
+
+
+图 1:一个场景看完整部署:服务是立方体,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 pill** 显示该服务的实时主吞吐:应用和 mesh 服务是 requests per minute,数据服务是 queries 或 operations per second,并保留各自单位。只有足够近、可读时 pill 才出现;缩远后会淡出,保持场景干净;选中的立方体总会显示它的数字。
+
+## 告警,以及 incident 用的 Beacon mode
+
+当一个服务有 **当前正在触发的告警** 时(Horizon 轮询最近 20 分钟,并只统计 service scope 下仍在触发的告警),它的立方体会 **红色脉冲**。这就是一个信标,即使隔着整个场景也能看到,而告警 feed 会独立刷新。
+
+在繁忙地图里,几百个立方体中的一个红点仍然可能难找,所以有 **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 看到的服务。它们表示 *身份*,不是流量,所以默认隐藏;选中立方体后只显示它自己的 relatives,并沿着 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..096457649f8
--- /dev/null
+++ b/content/zh/2026-06-22-horizon-ui-trace-explorer/index.md
@@ -0,0 +1,92 @@
+---
+title: "认识 Horizon UI · 6/17:Trace Explorer"
+date: 2026-06-22
+author: 吴晟
+description: "Horizon UI 系列第六篇:按 Layer 使用的分布式 Trace 探索器,支持分阶段条件、可框选的时延分布图、三种阅读同一条 Trace 的方式,以及与原生 Trace 并列的 Zipkin 标签页。"
+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** 标签页。
+
+## 为排查设计,而不是为 tailing 设计
+
+Traces 标签页是一个位于 **Layer 内部** 的分布式 Trace 探索器:选择服务,设置条件,然后阅读单条 Trace 的 span 时间线。它有意和控制台其他部分不一样,因为 Trace 是排查数据,不是实时 feed。
+
+它 **拥有自己的时间范围和条件**。它不跟随全局顶栏时间选择器,也不会自动刷新。你先把要找的条件摆好,再按 **Run query**;按之前不会取任何数据。第一次运行前,列表只显示 *"Pick your conditions, then click Run query."*。当你在追二十分钟前的一条坏 Trace 时,最不需要的就是页面每几秒自己往前滑,所以它不会这么做。
+
+## 条件表单,不是查询语言
+
+整个过滤界面都是结构化表单控件:select、数字范围、tag chips。条件会暂存在工具栏里,只在点击 **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 添加为可删除 chip,多个 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** 着色:错误为红色,成功为强调色。所以左上角、右上角那类高处红点,就是你要找的“又慢又失败”的区域。
+
+这张图本身也是过滤器。点击一个点可以选中它,或者 **拖一个矩形框选一片点**,结果列表会收窄到这批选择;header 切换成 *"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 的命名事件)。
+
+详情 header 会显示 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..f9f38a2f0f6
--- /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 Maps"
+date: 2026-06-23
+author: 吴晟
+description: "Horizon UI 系列第八篇:browser agent 上报的 JavaScript 错误流,以及让这些错误真正可用的能力:逐帧把生产环境混淆栈还原到原始文件、行、列、符号和源码片段。"
+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/)讲的是服务日志;这一篇讲 *用户* 遇到的错误,也就是 browser agent 上报的 JavaScript 异常,以及把这些错误从噪声变成可处理问题的关键能力。
+
+生产环境 JavaScript stack 基本不可读。代码经过压缩和打包后发布,浏览器只会报告错误出现在 `app.min.js:1:98412`,也就是一段机器生成代码里的位置,几乎不给你任何线索。这个功能的目的,是把这条 stack 沿着正确的 **source map** 走回你的源码:原始文件、行、列、符号名,以及出错位置附近的代码片段,逐帧还原。
+
+## 浏览器错误流
+
+在 **BROWSER** Layer 上,**Browser Logs** 标签页(屏幕上的标签名,它专门表示 JavaScript 错误流)会列出 browser agent 上报的内容。BROWSER Layer 会把槽位重命名成自己的语义:services 变成 **Applications**,instances 变成 **Versions**,endpoints 变成 **Pages**。这个 feed 的阅读方式类似 [Log Explorer](/zh/2026-06-23-horizon-ui-log-explorer/):可点击的 **category** legend 带计数,density histogram 位于日志流之上。每一行都有时间、**category**、page、app version 和错误消息;如果带有 minified `line:col`,也会显示成 chip。
+
+排查方式和 trace/log 标签页一致:它拥有自己的 **Time range**(全局顶栏暂停),你可以按 **Version**、**Page** 或 **Category** 收窄,然后点击 **Run query**。没有后台轮询把视图从你脚下挪走,也没有要学习的查询语言,只有结构化控件。点击一行后,它会在日志流里原地展开。
+
+
+图 1:browser agent 的错误流:按 category 组织、带图表,并限定到某个 app 版本和页面。</br>
+
+那个 minified `line:col` 就是问题的缩影。它是真实位置,但位置在 *构建后* 的 bundle 里,不在你的源码里。后面的能力就是为了解决它。
+
+## 从 minified 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>
+
+## 哪些错误带可解析 stack
+
+不是每类错误都有东西可翻译。**`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 字符串内部,由解析器提取。)
+
+## 怎么提供 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 当成敏感内容,只放在可信服务器上。)
+
+这种“有意手动”的选择,也划清了 **权限** 边界。查看错误、列出 maps、**解析** stack 都是读操作,由 `browser-errors:read` 控制;**上传或删除** map 是写操作,由 `source-map:write` 控制。所以只读 viewer 可以整天反混淆 stack,但没有权限改变已加载的 map 集合。读就是读,修改 map store 才是写。
+
+## 下一步去哪里
+
+字段参考,包括 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..4db118e40a4
--- /dev/null
+++ b/content/zh/2026-06-23-horizon-ui-log-explorer/index.md
@@ -0,0 +1,67 @@
+---
+title: "认识 Horizon UI · 7/17:Log Explorer"
+date: 2026-06-23
+author: 吴晟
+description: "Horizon UI 系列第七篇:两种日志界面,一种是已采集、已索引、可与 Trace 关联的存储日志流,带 level 直方图;另一种是按需实时 tail 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 的容器日志。这些不是存储日志:OAP 直接从 **Kubernetes API server** 读取它们(也就是 `kubectl logs` 那条路径),Horizon 展示窗口,然后丢弃。不会持久化,也不会进入 SkyWalking 日志存储。
+
+某个 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 错误又是另一类数据:它不是服务日志,而是 browser agent 上报的客户端错误事件,有自己的分类,也有自己的 **source-map de-obfuscation**,可以把混淆后的 `app.min.js:1:...` frame 还原到原始 `file:line`。这是 Browser Layer 上的独立标签页,会在这个系列的另一篇文章里讲。
+
+## 存储日志流
+
+打开一个有 **Logs** 标签页的 Layer,先在 header 里选择服务,最新日志流会按 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 以可删除 chip 形式保留。
+- **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` 格式 chip**,以及内容的一行预览。Horizon 按 payload 实际内容决定这个 chip:OAP 会标注 body 是 JSON 还是 plain text,在此基础上 Horizon 还会嗅探 JSON 和 YAML 结构,所以即使一行没有被标注但内容是结构化的,也会得到正确处理。JSON 在预览里压平成一行,YAML 保留 key,plain text 会折叠空白。
+
+
+图 1:某个服务的存储日志流:窗口上的 level histogram 和 level 计数,下面是每行日志,带 JSON / YAML / TEXT 标记并可跳到 Trace。</br>
+
+## 进入单行日志
+
+点击一行后,完整 payload 会在 popout 里打开:完整内容会按 **格式感知 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:完整查看一行日志:payload 按格式排版,旁边展示上下文和所有 tags。</br>
+
+## Pod Logs:tail 当前正在输出的内容
+
+**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** 后,窗口会流入只读 viewer,保持最新行可见,并持续轮询直到你按 **Pause**。header 条显示 container、行数、live indicator,以及上次更新距今多久。两行 **Include** / **Exclude** filter 用来收窄可见内容;每个 chip 都是一个 **整行正则表达式**(`.*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。