<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Honeycomb on Tarragon</title><link>https://tarrragon.github.io/blog/tags/honeycomb/</link><description>Recent content in Honeycomb on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 22 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/honeycomb/index.xml" rel="self" type="application/rss+xml"/><item><title>High-Cardinality Query Model 與 BubbleUp</title><link>https://tarrragon.github.io/blog/backend/04-observability/vendors/honeycomb/high-cardinality-query-bubbleup/</link><pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/04-observability/vendors/honeycomb/high-cardinality-query-bubbleup/</guid><description>&lt;blockquote>
&lt;p>本文是 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/vendors/honeycomb/" data-link-title="Honeycomb" data-link-desc="High-cardinality observability 平台、events-based 模型">Honeycomb&lt;/a> 的 vendor deep article，深化 overview「BubbleUp 分析」跟「Events vs metrics 心智模型」段。初次接觸 Honeycomb 的讀者建議先讀 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/vendors/honeycomb/" data-link-title="Honeycomb" data-link-desc="High-cardinality observability 平台、events-based 模型">Honeycomb 服務頁&lt;/a>。&lt;/p>&lt;/blockquote>
&lt;h2 id="問題情境">問題情境&lt;/h2>
&lt;p>Metrics-based 觀測系統有一個結構性限制：metric 在寫入前就做了 aggregation，之後只能沿著預先定義的 label 維度查詢。當事故需要按 user_id、request_id、feature_flag_variant 或 deployment_version 定位時，metrics 系統要嘛沒有這些維度（label cardinality 會爆），要嘛需要事先知道要看哪個維度（但事故通常是 unknown-unknowns）。&lt;/p>
&lt;p>Honeycomb 用 event-based 模型解決這個問題 — 每一筆 event（通常是一個 trace span）帶幾十個 attribute，查詢時才決定 group by 哪些維度。BubbleUp 進一步自動找出區隔 outlier 跟 baseline 的 attribute，讓工程師不需要事先猜測問題維度。&lt;/p>
&lt;p>理解 Honeycomb 的資料模型、查詢設計跟 BubbleUp 的工作方式，才能判斷什麼場景下 Honeycomb 比 metrics-first 系統更有效、什麼場景下 metrics-first 仍然是對的選擇。&lt;/p>
&lt;h2 id="核心概念">核心概念&lt;/h2>
&lt;h3 id="event-based-資料模型">Event-based 資料模型&lt;/h3>
&lt;p>Honeycomb 的儲存引擎是 column store — 每一筆 event 是一列、每一個 attribute 是一欄。寫入時不做 aggregation，查詢時才 group by / filter / aggregate。&lt;/p>
&lt;p>跟 metrics-first 系統的根本差異：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>面向&lt;/th>
 &lt;th>Metrics-first（Prometheus）&lt;/th>
 &lt;th>Event-based（Honeycomb）&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>寫入時&lt;/td>
 &lt;td>按 label 組合 aggregate 成 time series&lt;/td>
 &lt;td>存原始 event、帶所有 attribute&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>查詢時&lt;/td>
 &lt;td>只能沿既有 label 維度查詢&lt;/td>
 &lt;td>任意 attribute 組合 group by&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Cardinality&lt;/td>
 &lt;td>label 組合數 = time series 數、有上限&lt;/td>
 &lt;td>Attribute 組合數不影響儲存結構&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>成本模型&lt;/td>
 &lt;td>按 time series 數計費&lt;/td>
 &lt;td>按 events volume 計費&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>適合&lt;/td>
 &lt;td>已知維度的趨勢監控&lt;/td>
 &lt;td>unknown-unknowns 的事故偵錯&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>一筆 checkout event 在 Honeycomb 可能帶 30+ 個 attribute：service.name、http.method、http.status_code、http.url、user_id、tenant_id、region、deployment_version、feature_flag.variant、db.duration_ms、cache.hit、payment.provider、error.message 等。在 Prometheus 上，user_id 跟 tenant_id 是不能當 label 的（cardinality 爆）；在 Honeycomb 上，它們只是多一欄。&lt;/p>
&lt;h3 id="bubbleup-的工作方式">BubbleUp 的工作方式&lt;/h3>
&lt;p>BubbleUp 是 Honeycomb 的自動異常歸因功能。操作流程：&lt;/p>
&lt;ol>
&lt;li>在 heatmap 上框選異常區域（例如 latency spike 的時間段跟數值範圍）&lt;/li>
&lt;li>BubbleUp 把框選區域的 events（outlier set）跟框外 events（baseline set）做統計比較&lt;/li>
&lt;li>對每一個 attribute，計算兩組 events 的分布差異（Honeycomb 使用 distribution divergence 量度）&lt;/li>
&lt;li>排序差異最大的 attribute 顯示在面板上&lt;/li>
&lt;/ol>
&lt;p>BubbleUp 的價值在於它跳過了「猜測哪個維度有問題」的步驟。傳統 metrics dashboarding 需要工程師先想到「可能是某個 region 的問題」→ 加 region filter → 確認。BubbleUp 直接告訴你「outlier set 跟 baseline set 在 region、deployment_version、payment.provider 三個維度上分布最不同」。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是 <a href="/blog/backend/04-observability/vendors/honeycomb/" data-link-title="Honeycomb" data-link-desc="High-cardinality observability 平台、events-based 模型">Honeycomb</a> 的 vendor deep article，深化 overview「BubbleUp 分析」跟「Events vs metrics 心智模型」段。初次接觸 Honeycomb 的讀者建議先讀 <a href="/blog/backend/04-observability/vendors/honeycomb/" data-link-title="Honeycomb" data-link-desc="High-cardinality observability 平台、events-based 模型">Honeycomb 服務頁</a>。</p></blockquote>
<h2 id="問題情境">問題情境</h2>
<p>Metrics-based 觀測系統有一個結構性限制：metric 在寫入前就做了 aggregation，之後只能沿著預先定義的 label 維度查詢。當事故需要按 user_id、request_id、feature_flag_variant 或 deployment_version 定位時，metrics 系統要嘛沒有這些維度（label cardinality 會爆），要嘛需要事先知道要看哪個維度（但事故通常是 unknown-unknowns）。</p>
<p>Honeycomb 用 event-based 模型解決這個問題 — 每一筆 event（通常是一個 trace span）帶幾十個 attribute，查詢時才決定 group by 哪些維度。BubbleUp 進一步自動找出區隔 outlier 跟 baseline 的 attribute，讓工程師不需要事先猜測問題維度。</p>
<p>理解 Honeycomb 的資料模型、查詢設計跟 BubbleUp 的工作方式，才能判斷什麼場景下 Honeycomb 比 metrics-first 系統更有效、什麼場景下 metrics-first 仍然是對的選擇。</p>
<h2 id="核心概念">核心概念</h2>
<h3 id="event-based-資料模型">Event-based 資料模型</h3>
<p>Honeycomb 的儲存引擎是 column store — 每一筆 event 是一列、每一個 attribute 是一欄。寫入時不做 aggregation，查詢時才 group by / filter / aggregate。</p>
<p>跟 metrics-first 系統的根本差異：</p>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>Metrics-first（Prometheus）</th>
          <th>Event-based（Honeycomb）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>寫入時</td>
          <td>按 label 組合 aggregate 成 time series</td>
          <td>存原始 event、帶所有 attribute</td>
      </tr>
      <tr>
          <td>查詢時</td>
          <td>只能沿既有 label 維度查詢</td>
          <td>任意 attribute 組合 group by</td>
      </tr>
      <tr>
          <td>Cardinality</td>
          <td>label 組合數 = time series 數、有上限</td>
          <td>Attribute 組合數不影響儲存結構</td>
      </tr>
      <tr>
          <td>成本模型</td>
          <td>按 time series 數計費</td>
          <td>按 events volume 計費</td>
      </tr>
      <tr>
          <td>適合</td>
          <td>已知維度的趨勢監控</td>
          <td>unknown-unknowns 的事故偵錯</td>
      </tr>
  </tbody>
</table>
<p>一筆 checkout event 在 Honeycomb 可能帶 30+ 個 attribute：service.name、http.method、http.status_code、http.url、user_id、tenant_id、region、deployment_version、feature_flag.variant、db.duration_ms、cache.hit、payment.provider、error.message 等。在 Prometheus 上，user_id 跟 tenant_id 是不能當 label 的（cardinality 爆）；在 Honeycomb 上，它們只是多一欄。</p>
<h3 id="bubbleup-的工作方式">BubbleUp 的工作方式</h3>
<p>BubbleUp 是 Honeycomb 的自動異常歸因功能。操作流程：</p>
<ol>
<li>在 heatmap 上框選異常區域（例如 latency spike 的時間段跟數值範圍）</li>
<li>BubbleUp 把框選區域的 events（outlier set）跟框外 events（baseline set）做統計比較</li>
<li>對每一個 attribute，計算兩組 events 的分布差異（Honeycomb 使用 distribution divergence 量度）</li>
<li>排序差異最大的 attribute 顯示在面板上</li>
</ol>
<p>BubbleUp 的價值在於它跳過了「猜測哪個維度有問題」的步驟。傳統 metrics dashboarding 需要工程師先想到「可能是某個 region 的問題」→ 加 region filter → 確認。BubbleUp 直接告訴你「outlier set 跟 baseline set 在 region、deployment_version、payment.provider 三個維度上分布最不同」。</p>
<p>BubbleUp 的限制：它需要足夠的 event 量才能做統計比較。低 QPS 服務（&lt; 1 event/sec）在短時間窗內可能沒有足夠的 outlier events。它也不處理因果關係 — 分布差異最大的 attribute 不一定是 root cause，可能是 correlated symptom。</p>
<h3 id="slo-與-burn-rate-alert">SLO 與 Burn Rate Alert</h3>
<p>Honeycomb 的 SLO 功能把 service-level indicator 定義成一個 query、目標成功率定義成 SLO threshold、窗口跟 burn rate 用來觸發 alert。</p>
<p>SLO 設定要素：</p>
<ul>
<li><strong>SLI query</strong>：定義「成功」的條件。例如 <code>WHERE duration_ms &lt; 500 AND http.status_code &lt; 500</code>。</li>
<li><strong>SLO target</strong>：例如 99.9%。</li>
<li><strong>Window</strong>：通常 30 天 rolling window。</li>
<li><strong>Burn rate alert</strong>：multi-window multi-burn-rate。1 小時窗口看快速 burn（14.4x burn rate）、6 小時窗口看中速 burn（6x）、3 天窗口看慢速 burn（1x）。</li>
</ul>
<p>跟 Prometheus-based SLO 的差異：Prometheus SLO 通常用 recording rule 預先計算 error budget remaining，alert 基於 recording rule 結果。Honeycomb SLO 直接在 event 上做即時計算，不需要 recording rule。代價是 Honeycomb 的 SLO 計算跟平台綁定、不可搬。</p>
<p>對應 <a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn-rate</a> 概念跟 <a href="/blog/backend/04-observability/sli-slo-signal/" data-link-title="4.6 SLI 量測與 SLO 訊號設計" data-link-desc="把可靠性目標的訊號從 metric 端設計好、餵給 6.6 SLO 政策">4.6 SLI/SLO signal</a> 的訊號設計。</p>
<h2 id="配置-step-by-step">配置 step-by-step</h2>
<h3 id="derived-columns">Derived Columns</h3>
<p>Derived columns 是在 Honeycomb 查詢層建立的計算欄位，不改變原始 event。</p>
<p>常用場景：</p>
<ul>
<li><strong>Duration bucket</strong>：<code>IF(LTE($duration_ms, 100), &quot;fast&quot;, IF(LTE($duration_ms, 500), &quot;normal&quot;, &quot;slow&quot;))</code> — 把連續數值轉成 category、方便 group by</li>
<li><strong>Error classification</strong>：<code>IF(GTE($http.status_code, 500), &quot;server_error&quot;, IF(GTE($http.status_code, 400), &quot;client_error&quot;, &quot;ok&quot;))</code> — 對 status code 做語意分類</li>
<li><strong>Feature flag analysis</strong>：<code>CONCAT($service.name, &quot;-&quot;, $feature_flag.variant)</code> — 組合 attribute 做 A/B 比較</li>
</ul>
<p>Derived columns 的效能影響：它們在查詢時計算，不佔 ingestion 或 storage。但複雜的 derived column expression 會增加查詢 latency。</p>
<h3 id="dataset-設計">Dataset 設計</h3>
<p>Honeycomb 的 dataset 是資料隔離的單位。設計決策：</p>
<p><strong>Option A：per-environment dataset</strong>（production / staging / dev 各自獨立）。優點是查詢預設在單一環境、不需要每次加 environment filter。缺點是跨環境比較需要切換 dataset。</p>
<p><strong>Option B：per-service dataset</strong>（checkout-api / payment-adapter / notification-service 各自獨立）。優點是單一服務的查詢效能好（資料量小）。缺點是跨服務 trace 需要用 trace view 跨 dataset 查。</p>
<p><strong>Option C：single dataset per environment</strong>（production 一個大 dataset、所有服務混在一起）。優點是跨服務查詢不需切換、BubbleUp 能跨服務比較。缺點是資料量大、查詢稍慢、不同服務的 attribute 不一致可能造成混淆。</p>
<p>Honeycomb 推薦 Option C — 把同一環境的所有服務放同一個 dataset。理由是 BubbleUp 跟 trace view 的跨服務能力是 Honeycomb 的核心價值，拆太細會削弱這個優勢。用 <code>service.name</code> attribute 做 per-service filter。</p>
<h3 id="otlp-ingestion">OTLP Ingestion</h3>
<p>Honeycomb 原生接受 OTLP（gRPC 跟 HTTP）。應用程式用 OTel SDK 產生 traces / logs、設定 OTLP endpoint 為 <code>api.honeycomb.io:443</code>、帶 API key header。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl"># OTel Collector config example
</span></span><span class="line"><span class="ln">2</span><span class="cl">exporters:
</span></span><span class="line"><span class="ln">3</span><span class="cl">  otlp:
</span></span><span class="line"><span class="ln">4</span><span class="cl">    endpoint: &#34;api.honeycomb.io:443&#34;
</span></span><span class="line"><span class="ln">5</span><span class="cl">    headers:
</span></span><span class="line"><span class="ln">6</span><span class="cl">      &#34;x-honeycomb-team&#34;: &#34;${HONEYCOMB_API_KEY}&#34;
</span></span><span class="line"><span class="ln">7</span><span class="cl">      &#34;x-honeycomb-dataset&#34;: &#34;production&#34;</span></span></code></pre></div><p>OTel SDK 跟 Honeycomb Beeline SDK 的選擇：新部署一律用 OTel SDK — vendor neutral、可搬。Beeline SDK 是 Honeycomb-specific，已進入維護模式。既有 Beeline 部署可以逐步遷移到 OTel SDK。</p>
<h2 id="故障演練與邊界">故障演練與邊界</h2>
<h3 id="sampling-不足導致成本失控">Sampling 不足導致成本失控</h3>
<p><strong>觸發條件</strong>：高 QPS 服務（&gt; 10K req/sec）不做 sampling、全量送 Honeycomb。</p>
<p><strong>表現</strong>：月帳單高於預期。Honeycomb 按 events volume 計費、高 QPS 服務全量 ingestion 的成本可能是 Prometheus 的數倍。</p>
<p><strong>修復</strong>：部署 Refinery（Honeycomb 的 tail-based sampling proxy）。Refinery 在 trace 完成後決定是否保留 — 保留所有 error trace、保留所有高 latency trace、對正常 trace 做 sampling（例如保留 10%）。Dynamic sampling 根據 traffic pattern 自動調整 sampling rate。</p>
<p>成本與可見度的取捨：1% sampling 意味著 99% 的正常 event 看不到。如果需要回答「過去一小時有多少 successful request」這種 count 問題，sampling 會引入統計誤差。Honeycomb 支援 sample rate annotation — query 結果會用 sample rate 做加權還原。</p>
<h3 id="bubbleup-結果不可行動">BubbleUp 結果不可行動</h3>
<p><strong>觸發條件</strong>：BubbleUp 顯示差異最大的 attribute 是「timestamp」或「trace_id」— 這些 attribute 天然在 outlier set 跟 baseline set 之間分布不同，不提供歸因資訊。</p>
<p><strong>修復</strong>：在 BubbleUp 設定中排除 high-entropy attribute（trace_id、span_id、timestamp）。Honeycomb 允許設定 BubbleUp 的 ignore list。另外確保 event 帶足夠的 business-context attribute — 如果 event 只有 infra-level attribute（CPU、memory），BubbleUp 能找到的 insight 有限。</p>
<h3 id="gaming-高峰的-cardinality-情境">Gaming 高峰的 cardinality 情境</h3>
<p><a href="/blog/backend/04-observability/cases/gaming-peak-signal-freshness-and-cardinality/" data-link-title="Gaming：高峰流量下的訊號新鮮度與 Cardinality" data-link-desc="在高峰事件中控制訊號延遲與維度爆炸，維持告警與定位可信度。">Gaming 案例</a>揭露了 metrics-first 跟 event-first 系統在高峰期的根本差異。線上遊戲的賽季開跑或限時活動會讓流量在 30 分鐘內暴增 10 倍，同時 per-player、per-match-id 的 label 組合讓 Prometheus 的 active series 從 50 萬爆到 500 萬。</p>
<p>Prometheus 在這個場景的痛點不只是容量 — 而是 cardinality 爆炸改變了系統行為：scrape 變慢導致 metric freshness 從 15 秒退化到數分鐘、recording rule evaluation 跟不上 interval、alert 基於過期數據判斷。修法是 drop per-player label 或做 pre-aggregation、但 drop 掉之後事故時就查不到「哪個玩家的 session 異常」。</p>
<p>Honeycomb 的 event model 在這個場景天然有優勢 — per-player、per-match 是 event 上的 attribute，不產生 series、不影響 ingestion 效能。活動開跑時 event volume 暴增，但 Honeycomb 的 column store 只是行數增加、查詢的 IO 成本線性增長而非指數。BubbleUp 可以在高峰期直接找出「哪些 player_region × match_type 的組合延遲最高」。</p>
<p>代價是成本 — 10 倍的流量意味著 10 倍的 events volume、10 倍的計費。Gaming 場景通常需要搭配動態 sampling：正常 gameplay event 做 1:100 sampling、error 跟 high-latency event 全量保留。Refinery 的 tail-based sampling 在這裡是必備元件。</p>
<h3 id="honeycomb-vs-prometheus-的共存">Honeycomb vs Prometheus 的共存</h3>
<p>Honeycomb 不取代 Prometheus — 兩者解決不同問題。Prometheus 適合已知維度的趨勢監控（error rate dashboard、capacity trending、SLO burn rate），Honeycomb 適合 unknown-unknowns 的事故偵錯。</p>
<p>共存模式：application 用 OTel SDK 同時產生 metrics（→ Prometheus）跟 traces（→ Honeycomb）。Alerting 在 Prometheus 側（因為 metrics aggregation 穩定且成本低），深度偵錯在 Honeycomb 側。</p>
<h3 id="雙工具成本治理模式">雙工具成本治理模式</h3>
<p><a href="/blog/backend/04-observability/cases/observability-cost-governance-at-scale/" data-link-title="4.C14 觀測平台成本治理：從帳單驚嚇到可預測成本" data-link-desc="觀測帳單持續超線性成長時，用 cost attribution、cardinality budget、log tiering 跟 adaptive sampling 建立可預測成本模型。">觀測成本治理案例</a>提出一個在中大型團隊反覆驗證的分工：Prometheus 負責 golden signals（低 cardinality、固定 recording rules、成本可預測），Honeycomb 負責 high-cardinality debug（按需查詢、pay per event）。</p>
<p>這個分工的成本結構：Prometheus 的成本隨 active series 數量增長（cardinality-driven）、Honeycomb 的成本隨 event volume 增長（traffic-driven）。兩者的成本 driver 不同、scaling curve 不同 — Prometheus 在 series 爆炸時成本失控、Honeycomb 在 QPS 暴增時成本失控。把兩者放在一起、用各自的成本 sweet spot 互補、比只買一家更能控制總成本。</p>
<p>判讀自己是否需要雙工具的訊號：Prometheus dashboard 已經穩定、但事故時仍需要 20+ 分鐘才能定位到具體 user / request / deployment_version — 這 20 分鐘就是 Honeycomb 的價值。如果事故定位都能在 5 分鐘內靠 Prometheus label 完成，不需要加 Honeycomb。</p>
<h2 id="容量與成本">容量與成本</h2>
<p>Honeycomb 的計費基於 <strong>events volume</strong>（per million events ingested per month）。Event 的大小（attribute 數量）不直接影響計費（目前模型按 event 筆數、不按 payload size）。</p>
<p>成本治理手段：</p>
<ul>
<li><strong>Sampling</strong>：最直接。10% sampling = 成本降 90%。用 Refinery 做 tail-based sampling 保留重要 trace。</li>
<li><strong>Attribute 精簡</strong>：減少不需要的 attribute 不直接降成本（按筆數計費），但能加快查詢。</li>
<li><strong>Dataset 合併</strong>：多個小 dataset 合併成一個不影響成本，但能改善 BubbleUp 的統計品質。</li>
<li><strong>Team plan vs Enterprise</strong>：不同 plan 的 retention 跟 query 配額不同。</li>
</ul>
<p>跟 Prometheus 的成本比較：Prometheus 按 time series 數量計（self-host 的話是 infra 成本），Honeycomb 按 event 數量計。高 QPS + 低 cardinality 場景、Prometheus 成本優勢明顯。高 cardinality + 需要深度偵錯場景、Honeycomb 的 event cost 換到的是 BubbleUp 跟 arbitrary group by 的能力。</p>
<h3 id="不同規模的成本形態">不同規模的成本形態</h3>
<table>
  <thead>
      <tr>
          <th>規模</th>
          <th>月 event 量</th>
          <th>預估月成本範圍</th>
          <th>成本治理重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>小型（1-5 服務、&lt; 1K QPS）</td>
          <td>&lt; 50M events</td>
          <td>Free tier 或低帳單</td>
          <td>不需特別治理</td>
      </tr>
      <tr>
          <td>中型（10-30 服務、1-10K QPS）</td>
          <td>50M-500M events</td>
          <td>中等（依 plan）</td>
          <td>Refinery sampling 開始有 ROI</td>
      </tr>
      <tr>
          <td>大型（50+ 服務、10K+ QPS）</td>
          <td>1B+ events</td>
          <td>高（需要 Enterprise plan）</td>
          <td>Refinery + 動態 sampling 必備、跟 Prometheus 分工控制總成本</td>
      </tr>
  </tbody>
</table>
<p>大型場景的成本治理核心是 sampling 策略 — 全量 ingestion 的成本通常不可接受。Refinery 的 tail-based sampling 讓 error trace 跟 high-latency trace 全量保留、normal trace 做 1:10 到 1:100 sampling。Sampling rate 的選擇取決於「事故時需要多少正常 trace 做 baseline 比對」— BubbleUp 需要足夠的 baseline events 才能計算分布差異，sampling 太激進會讓 BubbleUp 的統計品質下降。</p>
<p>經驗值：保留至少 5-10% 的正常 trace、同時全量保留所有 error / slow trace。在 Gaming 案例的高峰期，正常 trace 的 sampling 可以暫時降到 1%（高峰流量 10 倍、1% sampling 仍有大量 baseline events），高峰結束後恢復到 10%。動態 sampling 根據當前 QPS 自動調整 — Refinery 的 <code>DynamicSampler</code> 會根據 key field（service.name + http.status_code）的分布自動決定 sample rate。</p>
<h2 id="整合與下一步">整合與下一步</h2>
<ul>
<li><a href="/blog/backend/04-observability/vendors/honeycomb/" data-link-title="Honeycomb" data-link-desc="High-cardinality observability 平台、events-based 模型">Honeycomb 服務頁</a>：overview 與日常操作</li>
<li><a href="/blog/backend/04-observability/cardinality-cost-governance/" data-link-title="4.7 Cardinality 治理與成本邊界" data-link-desc="把 metric / log / trace 的 cardinality 與成本作為平台一級治理議題">4.7 cardinality 治理</a>：cardinality 在 metrics-first 跟 event-first 系統的不同治理策略</li>
<li><a href="/blog/backend/04-observability/sli-slo-signal/" data-link-title="4.6 SLI 量測與 SLO 訊號設計" data-link-desc="把可靠性目標的訊號從 metric 端設計好、餵給 6.6 SLO 政策">4.6 SLI/SLO signal</a>：SLO / burn rate 的訊號設計</li>
<li><a href="/blog/backend/04-observability/vendors/opentelemetry/" data-link-title="OpenTelemetry" data-link-desc="可觀測性開放標準、SDK 與 Collector">OpenTelemetry</a>：OTLP ingestion 的上游標準</li>
<li><a href="/blog/backend/04-observability/vendors/prometheus/" data-link-title="Prometheus" data-link-desc="Pull-based metrics 主流 OSS、PromQL 與 alerting">Prometheus</a>：共存模式中的 metrics 面</li>
<li><a href="/blog/backend/04-observability/cases/gaming-peak-signal-freshness-and-cardinality/" data-link-title="Gaming：高峰流量下的訊號新鮮度與 Cardinality" data-link-desc="在高峰事件中控制訊號延遲與維度爆炸，維持告警與定位可信度。">4.C2 Gaming peak cardinality</a>：high-cardinality 場景的案例回寫</li>
</ul>
]]></content:encoded></item><item><title>Sentry → Honeycomb：trace 不是 error、是不同 observability paradigm</title><link>https://tarrragon.github.io/blog/backend/04-observability/vendors/honeycomb/migrate-from-sentry/</link><pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/04-observability/vendors/honeycomb/migrate-from-sentry/</guid><description>&lt;blockquote>
&lt;p>本文是跨 vendor migration playbook、cross-link &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/vendors/sentry/" data-link-title="Sentry" data-link-desc="Error tracking 主流、APM / Profiling / Session Replay 擴展">Sentry&lt;/a> 跟 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/vendors/honeycomb/" data-link-title="Honeycomb" data-link-desc="High-cardinality observability 平台、events-based 模型">Honeycomb&lt;/a>。跑 &lt;a href="https://tarrragon.github.io/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">migration-playbook-methodology 6 維 audit&lt;/a> 後對映 &lt;em>Paradigm = High（error tracking ↔ wide-event observability）→ Type E paradigm shift&lt;/em>。&lt;/p>&lt;/blockquote>
&lt;h2 id="trace-不是-error是不同-paradigm">Trace 不是 error、是不同 paradigm&lt;/h2>
&lt;p>把 Sentry → Honeycomb 當「trace tool 替換」是最常見的誤判 — Sentry trace 是 &lt;em>error 上下文&lt;/em>、Honeycomb trace 是 &lt;em>observability 第一性&lt;/em>：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>概念&lt;/th>
 &lt;th>Sentry&lt;/th>
 &lt;th>Honeycomb&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>核心 paradigm&lt;/td>
 &lt;td>Error tracking + transaction trace&lt;/td>
 &lt;td>High-cardinality wide-event observability&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>第一性 unit&lt;/td>
 &lt;td>Error event&lt;/td>
 &lt;td>Wide event (span with N fields)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Trace 角色&lt;/td>
 &lt;td>Error 的「附帶 context」&lt;/td>
 &lt;td>Observability 主軸、每 event 是 trace span&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Sampling&lt;/td>
 &lt;td>Error 全收 + transaction sample&lt;/td>
 &lt;td>Adaptive sampling、保留 &lt;em>anomaly&lt;/em>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Query model&lt;/td>
 &lt;td>Filter + group by + aggregation&lt;/td>
 &lt;td>High-cardinality 多維 query (BubbleUp / heatmap)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>User base&lt;/td>
 &lt;td>Developer (debug error)&lt;/td>
 &lt;td>SRE + Platform (debug system behavior)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Cost model&lt;/td>
 &lt;td>Per-error event + transaction&lt;/td>
 &lt;td>Per-event (wide event volume)&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>核心差異不在「Honeycomb 是 better Sentry」、在「兩者是不同 observability paradigm」&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>Sentry 適合 &lt;em>application-level error debug&lt;/em> — 拿到 error stack trace + minimal context、快速 fix&lt;/li>
&lt;li>Honeycomb 適合 &lt;em>system-level behavior debug&lt;/em> — 看流量分佈 / 多維 correlation / 異常 outlier、找 &lt;em>為什麼這個 user 在這個時段在這個 endpoint 慢&lt;/em>&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Migration scope 包含 &lt;em>paradigm reset&lt;/em> — 不是 SDK 換、是 SRE / Dev team 對 observability 的心智模型重設&lt;/strong>。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是跨 vendor migration playbook、cross-link <a href="/blog/backend/04-observability/vendors/sentry/" data-link-title="Sentry" data-link-desc="Error tracking 主流、APM / Profiling / Session Replay 擴展">Sentry</a> 跟 <a href="/blog/backend/04-observability/vendors/honeycomb/" data-link-title="Honeycomb" data-link-desc="High-cardinality observability 平台、events-based 模型">Honeycomb</a>。跑 <a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">migration-playbook-methodology 6 維 audit</a> 後對映 <em>Paradigm = High（error tracking ↔ wide-event observability）→ Type E paradigm shift</em>。</p></blockquote>
<h2 id="trace-不是-error是不同-paradigm">Trace 不是 error、是不同 paradigm</h2>
<p>把 Sentry → Honeycomb 當「trace tool 替換」是最常見的誤判 — Sentry trace 是 <em>error 上下文</em>、Honeycomb trace 是 <em>observability 第一性</em>：</p>
<table>
  <thead>
      <tr>
          <th>概念</th>
          <th>Sentry</th>
          <th>Honeycomb</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>核心 paradigm</td>
          <td>Error tracking + transaction trace</td>
          <td>High-cardinality wide-event observability</td>
      </tr>
      <tr>
          <td>第一性 unit</td>
          <td>Error event</td>
          <td>Wide event (span with N fields)</td>
      </tr>
      <tr>
          <td>Trace 角色</td>
          <td>Error 的「附帶 context」</td>
          <td>Observability 主軸、每 event 是 trace span</td>
      </tr>
      <tr>
          <td>Sampling</td>
          <td>Error 全收 + transaction sample</td>
          <td>Adaptive sampling、保留 <em>anomaly</em></td>
      </tr>
      <tr>
          <td>Query model</td>
          <td>Filter + group by + aggregation</td>
          <td>High-cardinality 多維 query (BubbleUp / heatmap)</td>
      </tr>
      <tr>
          <td>User base</td>
          <td>Developer (debug error)</td>
          <td>SRE + Platform (debug system behavior)</td>
      </tr>
      <tr>
          <td>Cost model</td>
          <td>Per-error event + transaction</td>
          <td>Per-event (wide event volume)</td>
      </tr>
  </tbody>
</table>
<p><strong>核心差異不在「Honeycomb 是 better Sentry」、在「兩者是不同 observability paradigm」</strong>：</p>
<ul>
<li>Sentry 適合 <em>application-level error debug</em> — 拿到 error stack trace + minimal context、快速 fix</li>
<li>Honeycomb 適合 <em>system-level behavior debug</em> — 看流量分佈 / 多維 correlation / 異常 outlier、找 <em>為什麼這個 user 在這個時段在這個 endpoint 慢</em></li>
</ul>
<p><strong>Migration scope 包含 <em>paradigm reset</em> — 不是 SDK 換、是 SRE / Dev team 對 observability 的心智模型重設</strong>。</p>
<h2 id="為什麼遷observability-成熟度--cardinality--cost-三條-driver">為什麼遷：observability 成熟度 / cardinality / cost 三條 driver</h2>
<table>
  <thead>
      <tr>
          <th>Driver</th>
          <th>觸發</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Observability 成熟度</td>
          <td>Application 規模到 <em>跨多 service / multi-tenant</em>、Sentry error tracking 不夠細、SRE 要看 <em>high-cardinality</em> 多維 query</td>
      </tr>
      <tr>
          <td>High-cardinality</td>
          <td>Sentry tag system 限制 cardinality（~1000 unique value）、Honeycomb native 支援 millions cardinality</td>
      </tr>
      <tr>
          <td>Cost</td>
          <td>Per-error pricing 對 high-error volume 場景爆、Honeycomb per-event 在 <em>wide event</em> 場景更可預測</td>
      </tr>
  </tbody>
</table>
<p>反向 driver（Honeycomb → Sentry）：</p>
<ul>
<li>Pure error tracking 場景、Honeycomb wide-event 過度設計</li>
<li>Frontend / mobile 客戶端 error tracking、Sentry 對 web/mobile/desktop SDK 成熟度高</li>
</ul>
<h2 id="6-維-audit">6 維 audit</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>等級</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema / API</td>
          <td>Medium（event schema 概念不同、SDK 完全換）</td>
      </tr>
      <tr>
          <td>Operational</td>
          <td>Low（兩者都 SaaS、operational 對等）</td>
      </tr>
      <tr>
          <td>Paradigm</td>
          <td><strong>High</strong>（error tracking ↔ wide-event observability）</td>
      </tr>
      <tr>
          <td>Components</td>
          <td>Low（同 1 個 observability vendor）</td>
      </tr>
      <tr>
          <td>Application change</td>
          <td><strong>High</strong>（SDK 換 + instrumentation 重設計）</td>
      </tr>
      <tr>
          <td>Data topology</td>
          <td>Low</td>
      </tr>
  </tbody>
</table>
<p>Paradigm = High（其他 Low-Medium）→ Type E paradigm shift；application change 雖 High 但是 paradigm 的 downstream。</p>
<h2 id="結構partial-migration--混合架構是-long-term-default">結構：partial migration + 混合架構是 long-term default</h2>
<p>跟 <a href="/blog/backend/03-message-queue/vendors/kafka/migrate-from-to-nats/" data-link-title="Kafka ↔ NATS：不是 migration、是 messaging paradigm 重設計" data-link-desc="Kafka 跟 NATS 不是同類產品（log-based event streaming vs subject-based messaging）、&#39;migration&#39; 字面上不成立；本文釐清兩家 paradigm 邊界、什麼情境真的能換、application 模式重設計的 5 個踩雷（consumer offset 觀念差 / retention model / exactly-once 假設 / schema registry 缺位 / fan-out 模式差）、跟 JetStream 對位 &#43; 混合架構">Kafka ↔ NATS</a> / <a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-memcached/" data-link-title="Redis → Memcached：Memcached 不是 simpler Redis、是 cache paradigm" data-link-desc="Redis → Memcached 是 Type E paradigm reduction migration — 從 multi-paradigm（KV &#43; 資料結構 &#43; pub/sub &#43; Lua &#43; streams）退到 pure cache；不是「remove Redis features」、是「重新分配 Redis-specific feature 到對應 specialized 服務」；5 個 production 踩雷 &#43; paradigm reduction 路線">Redis → Memcached</a> 同 Type E pattern：</p>
<ul>
<li><strong>不存在 complete migration</strong>：Sentry 對 <em>frontend error tracking</em> 強項、Honeycomb 對 <em>backend system observability</em> 強項</li>
<li><strong>長期混合架構</strong>：frontend / mobile 保留 Sentry、backend / SRE 走 Honeycomb</li>
<li><strong>Application 重設計</strong>：instrumentation 用 OpenTelemetry、避免 vendor SDK lock-in</li>
</ul>
<h2 id="application-重設計範例">Application 重設計範例</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># Before: Sentry SDK</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="kn">import</span> <span class="nn">sentry_sdk</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">sentry_sdk</span><span class="o">.</span><span class="n">init</span><span class="p">(</span><span class="n">dsn</span><span class="o">=</span><span class="s1">&#39;https://x@sentry.io/y&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">process_order</span><span class="p">(</span><span class="n">order_id</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="n">sentry_sdk</span><span class="o">.</span><span class="n">capture_exception</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">raise</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"># After: OpenTelemetry + Honeycomb</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="kn">from</span> <span class="nn">opentelemetry</span> <span class="kn">import</span> <span class="n">trace</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="kn">from</span> <span class="nn">opentelemetry.sdk.trace</span> <span class="kn">import</span> <span class="n">TracerProvider</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="kn">from</span> <span class="nn">opentelemetry.sdk.trace.export</span> <span class="kn">import</span> <span class="n">BatchSpanProcessor</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="kn">from</span> <span class="nn">opentelemetry.exporter.otlp.proto.grpc.trace_exporter</span> <span class="kn">import</span> <span class="n">OTLPSpanExporter</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="n">trace</span><span class="o">.</span><span class="n">set_tracer_provider</span><span class="p">(</span><span class="n">TracerProvider</span><span class="p">())</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="n">trace</span><span class="o">.</span><span class="n">get_tracer_provider</span><span class="p">()</span><span class="o">.</span><span class="n">add_span_processor</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="n">BatchSpanProcessor</span><span class="p">(</span><span class="n">OTLPSpanExporter</span><span class="p">(</span><span class="n">endpoint</span><span class="o">=</span><span class="s1">&#39;https://api.honeycomb.io&#39;</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="p">{</span><span class="s1">&#39;x-honeycomb-team&#39;</span><span class="p">:</span> <span class="s1">&#39;YOUR_API_KEY&#39;</span><span class="p">}))</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="n">tracer</span> <span class="o">=</span> <span class="n">trace</span><span class="o">.</span><span class="n">get_tracer</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">with</span> <span class="n">tracer</span><span class="o">.</span><span class="n">start_as_current_span</span><span class="p">(</span><span class="s1">&#39;process_order&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">span</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="n">span</span><span class="o">.</span><span class="n">set_attribute</span><span class="p">(</span><span class="s1">&#39;order.id&#39;</span><span class="p">,</span> <span class="n">order_id</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="n">span</span><span class="o">.</span><span class="n">set_attribute</span><span class="p">(</span><span class="s1">&#39;user.id&#39;</span><span class="p">,</span> <span class="n">user_id</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="n">span</span><span class="o">.</span><span class="n">set_attribute</span><span class="p">(</span><span class="s1">&#39;order.amount&#39;</span><span class="p">,</span> <span class="n">order</span><span class="o">.</span><span class="n">amount</span><span class="p">)</span>  <span class="c1"># high-cardinality 自然</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="n">span</span><span class="o">.</span><span class="n">set_attribute</span><span class="p">(</span><span class="s1">&#39;order.region&#39;</span><span class="p">,</span> <span class="n">region</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="n">process_order</span><span class="p">(</span><span class="n">order_id</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">        <span class="n">span</span><span class="o">.</span><span class="n">set_status</span><span class="p">(</span><span class="n">trace</span><span class="o">.</span><span class="n">Status</span><span class="p">(</span><span class="n">trace</span><span class="o">.</span><span class="n">StatusCode</span><span class="o">.</span><span class="n">OK</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">        <span class="n">span</span><span class="o">.</span><span class="n">set_status</span><span class="p">(</span><span class="n">trace</span><span class="o">.</span><span class="n">Status</span><span class="p">(</span><span class="n">trace</span><span class="o">.</span><span class="n">StatusCode</span><span class="o">.</span><span class="n">ERROR</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)))</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">        <span class="n">span</span><span class="o">.</span><span class="n">record_exception</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">        <span class="k">raise</span></span></span></code></pre></div><p>差異：</p>
<ul>
<li>Sentry 只 capture exception + 簡 context</li>
<li>Honeycomb 對每 operation 寫 <em>wide event</em> 含 high-cardinality field（user.id / order.amount / order.region）</li>
<li>SRE 端能跑 <code>WHERE order.region = &quot;us-west-2&quot; AND duration &gt; 5000</code> 的 multi-dim query</li>
</ul>
<h2 id="migration-流程">Migration 流程</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">1. Audit application：列所有 Sentry SDK 使用 + capture pattern
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">2. 分類處理 plan:
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">   - Pure error tracking (frontend): 保留 Sentry
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">   - Backend system trace: 切 Honeycomb / OTel
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">   - Error + context (混合): 雙寫期 evaluate
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">3. OpenTelemetry instrumentation 化:
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">   - 用 OTel SDK 取代 vendor SDK
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">   - Honeycomb 是 OTLP target、跟 vendor lock 解耦
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">4. Backend application 切 Honeycomb (3-6 個月)
</span></span><span class="line"><span class="ln">10</span><span class="cl">5. Frontend / mobile 保留 Sentry
</span></span><span class="line"><span class="ln">11</span><span class="cl">6. SRE training: Honeycomb BubbleUp / heatmap / multi-dim query</span></span></code></pre></div><h2 id="production-故障演練">Production 故障演練</h2>
<h3 id="case-1event-schema-對位失敗sre-不會用-bubbleup">Case 1：Event schema 對位失敗、SRE 不會用 BubbleUp</h3>
<p><strong>徵兆</strong>：切 Honeycomb 後 SRE 用 Sentry 思維 — 找 error → fix；Honeycomb BubbleUp / heatmap 沒人會用、observability 退化到 <em>只看 error count</em>。</p>
<p><strong>根因</strong>：Sentry → Honeycomb migration 不只是 tool 換、是 <em>observability mindset 換</em>；SRE 沒培訓 wide-event query / BubbleUp anomaly detection。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>SRE training</strong>：1-2 週 hands-on Honeycomb BubbleUp + heatmap + multi-dim query</li>
<li><strong>Migration scope 含 sample query playbook</strong>：每個 incident type 對應 Honeycomb query 寫成 runbook</li>
<li><strong>保留 Sentry frontend / mobile</strong>：不要逼 SRE 全切、保留 <em>paradigm fit</em> 的部分</li>
</ol>
<h3 id="case-2sampling-行為差production-cost-飛">Case 2：Sampling 行為差、production cost 飛</h3>
<p><strong>徵兆</strong>：切 Honeycomb 後第 1 個月 event volume 比 Sentry 高 100x；帳單暴漲。</p>
<p><strong>根因</strong>：Sentry 對 transaction 端 sample（10% 預設）、error 全收；Honeycomb 端 <em>每 span 都 wide event</em>、application 端沒設 sampling 全送、event volume 爆。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>Honeycomb Refinery (sampling proxy)</strong>：deploy refinery 在 application 端跟 Honeycomb 之間、tail-based sampling</li>
<li><strong>Sample rule</strong>：保留 <em>anomaly</em> (error / slow / outlier)、drop <em>boring success</em> 90%+</li>
<li><strong>Cost monitoring 第一週密集</strong>：cardinality + event volume + cost dashboard、catch 預期外 spike</li>
</ol>
<h3 id="case-3error-grouping-失效">Case 3：Error grouping 失效</h3>
<p><strong>徵兆</strong>：切 Honeycomb 後 <em>相似 error</em> 沒被 group 成「同類 issue」、SRE 看每 event 獨立、failure 模式淹沒在 noise。</p>
<p><strong>根因</strong>：Sentry 自動 error grouping (by stack trace fingerprint)、Honeycomb 沒對等 — wide event 是 first-class、event grouping 需要 application 端 explicit 設 <code>error.type</code> field。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>Application 端設 error type field</strong>：<code>span.set_attribute('error.type', exception_class)</code></li>
<li><strong>Honeycomb derived column</strong>：用 derived column 算 error fingerprint</li>
<li><strong>保留 Sentry error tracking</strong>：純 error grouping 場景 Sentry 強項、別硬切</li>
</ol>
<h3 id="case-4cost-模型差預估錯">Case 4：Cost 模型差、預估錯</h3>
<p><strong>徵兆</strong>：切 Honeycomb 後預估 50% cost saving、實際只省 10-15%。</p>
<p><strong>根因</strong>：Sentry per-error pricing 對 error-heavy application 貴；Honeycomb per-event pricing 對 <em>wide event volume</em> application 貴；如果 application 是 <em>event volume 高 但 error 少</em>、Honeycomb 反而貴。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>Pre-migration 估</strong>：用 OTel pilot 跑 1-2 週、估真實 event volume</li>
<li><strong>Sample rule 設計</strong>：retention 7 天 hot + 30 天 cold + 1 年 archive、降 cost</li>
<li><strong>混合架構保留</strong>：frontend / mobile 走 Sentry、backend 走 Honeycomb、避免一邊 cost 爆</li>
</ol>
<h3 id="case-5alert-paradigm-不對等">Case 5：Alert paradigm 不對等</h3>
<p><strong>徵兆</strong>：Sentry alert 簡單（error rate / latency p99 threshold）、Honeycomb trigger 配置複雜（SLO + burn rate + BubbleUp）；SOC 學習曲線 1-2 個月。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>Migration 含 alert rebuild scope</strong>：Honeycomb trigger 不直接對位 Sentry alert、要重寫</li>
<li><strong>SLO-driven alert</strong>：用 Honeycomb SLO 取代 Sentry threshold alert、降 alert fatigue</li>
<li><strong>PagerDuty integration</strong>：兩家都支援、routing rule 跟 dedup 要 review</li>
</ol>
<h2 id="capacity--cost">Capacity / cost</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Sentry</th>
          <th>Honeycomb</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Pricing model</td>
          <td>Per-error + transaction</td>
          <td>Per-event (wide event)</td>
      </tr>
      <tr>
          <td>Cost (mid-tier)</td>
          <td>$500-2000 / mo</td>
          <td>$400-3000 / mo (依 event volume)</td>
      </tr>
      <tr>
          <td>Sampling</td>
          <td>Built-in transaction sampling</td>
          <td>Refinery (additional component)</td>
      </tr>
      <tr>
          <td>Cardinality</td>
          <td>~1000 unique value / tag</td>
          <td>Millions / field</td>
      </tr>
      <tr>
          <td>Application complexity</td>
          <td>Low (SDK + capture exception)</td>
          <td>Medium (OTel + wide event instrument)</td>
      </tr>
      <tr>
          <td>Migration cost</td>
          <td>-</td>
          <td>2-4 FTE × 2-3 個月</td>
      </tr>
  </tbody>
</table>
<h2 id="整合--下一步">整合 / 下一步</h2>
<h3 id="跟-opentelemetry-整合">跟 OpenTelemetry 整合</h3>
<p>OTel 是 vendor-neutral instrumentation、Honeycomb 是 OTLP backend；application 端 OTel 化後可以同時 ship 到多個 backend（dev 端 Jaeger / production 端 Honeycomb / fallback 端 Tempo）。</p>
<h3 id="跟-datadog--grafana-stack-對位">跟 <a href="/blog/backend/04-observability/vendors/datadog/migrate-to-grafana-stack/" data-link-title="Datadog → Grafana Stack：把 $50K/month bill 拆解到 self-hosted observability" data-link-desc="Datadog 五層計費（host APM / metric / log ingest / log retention / RUM）拆解、對位 Grafana Stack（Mimir / Loki / Tempo / Grafana / Alloy）的 5 層責任；OTel-based agent migration、5 個 production 踩雷（cardinality 爆 / log volume cost / dashboard 不直接轉 / alert routing 換邏輯 / SLO definition 差異）、cost reality check">Datadog → Grafana Stack</a> 對位</h3>
<p>兩條 observability 路線：</p>
<ul>
<li>Grafana Stack (Mimir / Loki / Tempo)：self-host or Grafana Cloud、open source baseline</li>
<li>Honeycomb：SaaS-only、focus wide-event observability</li>
</ul>
<p>選擇取決於 <em>observability paradigm</em>：trace-heavy 走 Tempo / Honeycomb、metric-heavy 走 Mimir / Datadog。</p>
<h2 id="相關連結">相關連結</h2>
<ul>
<li>Source vendor：<a href="/blog/backend/04-observability/vendors/sentry/" data-link-title="Sentry" data-link-desc="Error tracking 主流、APM / Profiling / Session Replay 擴展">Sentry</a></li>
<li>Target vendor：<a href="/blog/backend/04-observability/vendors/honeycomb/" data-link-title="Honeycomb" data-link-desc="High-cardinality observability 平台、events-based 模型">Honeycomb</a></li>
<li>平行 migration playbook (Type E)：<a href="/blog/backend/03-message-queue/vendors/kafka/migrate-from-to-nats/" data-link-title="Kafka ↔ NATS：不是 migration、是 messaging paradigm 重設計" data-link-desc="Kafka 跟 NATS 不是同類產品（log-based event streaming vs subject-based messaging）、&#39;migration&#39; 字面上不成立；本文釐清兩家 paradigm 邊界、什麼情境真的能換、application 模式重設計的 5 個踩雷（consumer offset 觀念差 / retention model / exactly-once 假設 / schema registry 缺位 / fan-out 模式差）、跟 JetStream 對位 &#43; 混合架構">Kafka ↔ NATS</a> / <a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-memcached/" data-link-title="Redis → Memcached：Memcached 不是 simpler Redis、是 cache paradigm" data-link-desc="Redis → Memcached 是 Type E paradigm reduction migration — 從 multi-paradigm（KV &#43; 資料結構 &#43; pub/sub &#43; Lua &#43; streams）退到 pure cache；不是「remove Redis features」、是「重新分配 Redis-specific feature 到對應 specialized 服務」；5 個 production 踩雷 &#43; paradigm reduction 路線">Redis → Memcached</a> / <a href="/blog/backend/05-deployment-platform/vendors/consul/migrate-from-etcd/" data-link-title="etcd → Consul：KV &#43; N 個 extras feature matrix" data-link-desc="etcd → Consul 是 Type E paradigm shift expansion — 從 pure KV store 升到 service mesh / discovery / health check / multi-DC；本文用對照表 &#43; paradigm expansion 路線、5 個 production 踩雷（API 對位 / lock semantics / watch event model / multi-DC topology / ACL system）">etcd → Consul</a></li>
<li>Methodology：<a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">Migration playbook methodology</a></li>
</ul>
]]></content:encoded></item></channel></rss>