<?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>Rum on Tarragon</title><link>https://tarrragon.github.io/blog/tags/rum/</link><description>Recent content in Rum 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/rum/index.xml" rel="self" type="application/rss+xml"/><item><title>Datadog RUM</title><link>https://tarrragon.github.io/blog/monitoring/06-commercial-comparison/datadog-rum/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/06-commercial-comparison/datadog-rum/</guid><description>&lt;blockquote>
&lt;p>&lt;strong>跟 Backend 04 的分工&lt;/strong>：本文從 client-side RUM 角度說明 Datadog 的全棧追蹤、四種 RUM 事件與 session replay。Server-side 的 APM 平台治理（agent 配置、成本治理、OTel 相容遷移、從 New Relic 或 Grafana Stack 遷移）見 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/vendors/datadog/" data-link-title="Datadog" data-link-desc="All-in-one SaaS 觀測平台、APM / Logs / Metrics / RUM / Security">Backend 04 Datadog vendor page&lt;/a>。&lt;/p>&lt;/blockquote>
&lt;p>Datadog Real User Monitoring（RUM）從全棧 APM 的角度設計 client-side 監控。核心特徵是 client 端的使用者操作可以關聯到 server 端的 trace，形成從按鈕點擊到 database query 的完整請求鏈路。&lt;/p>
&lt;h2 id="全棧追蹤">全棧追蹤&lt;/h2>
&lt;p>Datadog RUM 的 SDK 在 HTTP 請求中自動注入 trace context header。Server 端的 Datadog APM agent 讀取 header，把 server 端的 trace 和 client 端的 action 關聯。&lt;/p>
&lt;p>這個能力在 debug「API 慢」的問題時特別有用 — 從 client 端看到「這個按鈕的回應時間 3 秒」，點進去看到 server 端的 trace 顯示「database query 佔了 2.8 秒」。自架方案和 Sentry 都做不到這個深度的跨層關聯。&lt;/p>
&lt;p>前提是 server 端也使用 Datadog APM。如果 server 端用其他 APM（New Relic、Elastic APM），client-server 的關聯需要自行實作或用 OpenTelemetry 橋接。&lt;/p>
&lt;h2 id="四種-rum-事件">四種 RUM 事件&lt;/h2>
&lt;p>Datadog RUM 收集四種事件，和自架方案的四類事件有對應關係（&lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/commercial-event-mapping/" data-link-title="商業方案的事件類型對應" data-link-desc="Sentry / Crashlytics / GA4 / Datadog RUM 各自如何對應四類事件 — 理解商業方案的分類邏輯才能正確接入">模組一 商業方案對應&lt;/a>）：&lt;/p>
&lt;p>&lt;strong>View&lt;/strong>：頁面或畫面的載入和離開。自動偵測 SPA 的 route 變換，對應 lifecycle 事件。&lt;/p>
&lt;p>&lt;strong>Action&lt;/strong>：使用者操作。自動捕獲 click、tap、scroll，可手動記錄自訂 action，對應 event 事件。&lt;/p>
&lt;p>&lt;strong>Error&lt;/strong>：JS exception、network error、自訂 error，對應 error 事件。&lt;/p>
&lt;p>&lt;strong>Long Task&lt;/strong>：執行時間超過 50ms 的任務（阻塞主執行緒），對應 metric 事件。&lt;/p>
&lt;h2 id="定價">定價&lt;/h2>
&lt;p>Datadog RUM 按 session 數計費（每個 session 是一次使用者訪問）。和 Sentry 按事件數計費不同 — session 計費讓成本更可預測（不會因為單次訪問觸發大量事件而費用暴增）。&lt;/p>
&lt;p>Datadog 的完整方案（RUM + APM + Logs + Infrastructure）費用較高，適合已經用 Datadog 做 server-side 監控的團隊。單獨用 RUM 而 server 端用其他方案，失去全棧追蹤的優勢。&lt;/p>
&lt;p>Datadog RUM 的全棧追蹤能力獨一無二，但如果只需要行為分析而非 APM，&lt;a href="https://tarrragon.github.io/blog/monitoring/06-commercial-comparison/mixpanel-amplitude/" data-link-title="Mixpanel / Amplitude" data-link-desc="行為分析專用方案 vs 通用監控的差異 — Mixpanel 和 Amplitude 的 funnel / cohort / retention 分析能力">Mixpanel / Amplitude&lt;/a> 是更輕量的選擇。和 &lt;a href="https://tarrragon.github.io/blog/monitoring/06-commercial-comparison/sentry-deep-dive/" data-link-title="Sentry 深入" data-link-desc="Error tracking &amp;#43; performance monitoring &amp;#43; session replay 的架構 — Sentry 從 error-first 出發如何擴展到全面可觀測性">Sentry&lt;/a> 的定位差異在於 Sentry 聚焦 error tracking、Datadog 聚焦全棧關聯。&lt;a href="https://tarrragon.github.io/blog/monitoring/06-commercial-comparison/self-hosted-vs-commercial/" data-link-title="自架 vs 商業的判斷決策表" data-link-desc="使用者數、網路範圍、功能需求、合規要求四個維度判斷該自架還是用商業方案">自架 vs 商業的判斷決策表&lt;/a>從使用者規模和功能需求維度做系統性比較。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p><strong>跟 Backend 04 的分工</strong>：本文從 client-side RUM 角度說明 Datadog 的全棧追蹤、四種 RUM 事件與 session replay。Server-side 的 APM 平台治理（agent 配置、成本治理、OTel 相容遷移、從 New Relic 或 Grafana Stack 遷移）見 <a href="/blog/backend/04-observability/vendors/datadog/" data-link-title="Datadog" data-link-desc="All-in-one SaaS 觀測平台、APM / Logs / Metrics / RUM / Security">Backend 04 Datadog vendor page</a>。</p></blockquote>
<p>Datadog Real User Monitoring（RUM）從全棧 APM 的角度設計 client-side 監控。核心特徵是 client 端的使用者操作可以關聯到 server 端的 trace，形成從按鈕點擊到 database query 的完整請求鏈路。</p>
<h2 id="全棧追蹤">全棧追蹤</h2>
<p>Datadog RUM 的 SDK 在 HTTP 請求中自動注入 trace context header。Server 端的 Datadog APM agent 讀取 header，把 server 端的 trace 和 client 端的 action 關聯。</p>
<p>這個能力在 debug「API 慢」的問題時特別有用 — 從 client 端看到「這個按鈕的回應時間 3 秒」，點進去看到 server 端的 trace 顯示「database query 佔了 2.8 秒」。自架方案和 Sentry 都做不到這個深度的跨層關聯。</p>
<p>前提是 server 端也使用 Datadog APM。如果 server 端用其他 APM（New Relic、Elastic APM），client-server 的關聯需要自行實作或用 OpenTelemetry 橋接。</p>
<h2 id="四種-rum-事件">四種 RUM 事件</h2>
<p>Datadog RUM 收集四種事件，和自架方案的四類事件有對應關係（<a href="/blog/monitoring/01-mental-model/commercial-event-mapping/" data-link-title="商業方案的事件類型對應" data-link-desc="Sentry / Crashlytics / GA4 / Datadog RUM 各自如何對應四類事件 — 理解商業方案的分類邏輯才能正確接入">模組一 商業方案對應</a>）：</p>
<p><strong>View</strong>：頁面或畫面的載入和離開。自動偵測 SPA 的 route 變換，對應 lifecycle 事件。</p>
<p><strong>Action</strong>：使用者操作。自動捕獲 click、tap、scroll，可手動記錄自訂 action，對應 event 事件。</p>
<p><strong>Error</strong>：JS exception、network error、自訂 error，對應 error 事件。</p>
<p><strong>Long Task</strong>：執行時間超過 50ms 的任務（阻塞主執行緒），對應 metric 事件。</p>
<h2 id="定價">定價</h2>
<p>Datadog RUM 按 session 數計費（每個 session 是一次使用者訪問）。和 Sentry 按事件數計費不同 — session 計費讓成本更可預測（不會因為單次訪問觸發大量事件而費用暴增）。</p>
<p>Datadog 的完整方案（RUM + APM + Logs + Infrastructure）費用較高，適合已經用 Datadog 做 server-side 監控的團隊。單獨用 RUM 而 server 端用其他方案，失去全棧追蹤的優勢。</p>
<p>Datadog RUM 的全棧追蹤能力獨一無二，但如果只需要行為分析而非 APM，<a href="/blog/monitoring/06-commercial-comparison/mixpanel-amplitude/" data-link-title="Mixpanel / Amplitude" data-link-desc="行為分析專用方案 vs 通用監控的差異 — Mixpanel 和 Amplitude 的 funnel / cohort / retention 分析能力">Mixpanel / Amplitude</a> 是更輕量的選擇。和 <a href="/blog/monitoring/06-commercial-comparison/sentry-deep-dive/" data-link-title="Sentry 深入" data-link-desc="Error tracking &#43; performance monitoring &#43; session replay 的架構 — Sentry 從 error-first 出發如何擴展到全面可觀測性">Sentry</a> 的定位差異在於 Sentry 聚焦 error tracking、Datadog 聚焦全棧關聯。<a href="/blog/monitoring/06-commercial-comparison/self-hosted-vs-commercial/" data-link-title="自架 vs 商業的判斷決策表" data-link-desc="使用者數、網路範圍、功能需求、合規要求四個維度判斷該自架還是用商業方案">自架 vs 商業的判斷決策表</a>從使用者規模和功能需求維度做系統性比較。</p>
]]></content:encoded></item><item><title>4.24 Client-to-Server 端到端觀測串接</title><link>https://tarrragon.github.io/blog/backend/04-observability/client-server-trace-integration/</link><pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/04-observability/client-server-trace-integration/</guid><description>&lt;p>Client-to-server 端到端觀測串接的核心責任是讓一次使用者操作的完整路徑 — 從 browser click 到 server 處理到 response rendering — 可以用同一個 trace ID 串起來。&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/client-side-monitoring/" data-link-title="4.10 Client-side / Synthetic / RUM" data-link-desc="補 server-side 看不到的 user perceived 訊號">4.10 Client-side / Synthetic / RUM&lt;/a> 講的是概念和 vendor 定位；本篇走完一個具體場景的實作鏈路。&lt;a href="https://tarrragon.github.io/blog/monitoring/03-sdk-design/" data-link-title="模組三：SDK 設計模式" data-link-desc="跨平台 SDK 的自動攔截、手動上報、攢批送出、離線 buffer 設計">Monitoring 模組 03 SDK 設計&lt;/a> 講的是 client 端怎麼埋點；本篇講 server 端怎麼接收和整合。&lt;/p>
&lt;h2 id="完整鏈路">完整鏈路&lt;/h2>
&lt;p>以使用者在 web app 點擊「結帳」為例，一次操作產生的觀測鏈路：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">Browser: user clicks &amp;#34;checkout&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl"> → RUM SDK 建立 client span（type: resource / xhr）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> → HTTP POST /api/checkout + W3C traceparent header
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> → Server middleware 提取 trace context
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> → Server 建立 child span（checkout-handler）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> → DB query span（order insert）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> → Cache span（inventory check）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> → Queue span（event publish）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> → Server 回 200 + response body
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> → Browser 收到 response → resource timing 結束
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> → RUM SDK 關閉 client span（記錄 duration + status）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> → 統一 trace waterfall：client span 是 root、server spans 是 children&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>鏈路的每一段都需要 trace context 正確傳遞。任何一段斷掉，trace waterfall 就會出現孤立的 span — server 端看到的 trace 跟 client 端看到的 trace 是兩條不相關的紀錄。&lt;/p>
&lt;h2 id="trace-context-propagation">Trace context propagation&lt;/h2>
&lt;h3 id="w3c-traceparent-header">W3C traceparent header&lt;/h3>
&lt;p>W3C Trace Context 是跨 vendor 的標準 propagation 格式。Header 長這樣：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> │ │ │ │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> │ trace-id (32 hex) parent-id (16 hex) flags
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl"> version&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>RUM SDK 在發起 XHR / fetch 時把 &lt;code>traceparent&lt;/code> 注入 request header。Server 的 trace SDK 從 header 提取 trace-id 和 parent-id，建立 child span。&lt;/p></description><content:encoded><![CDATA[<p>Client-to-server 端到端觀測串接的核心責任是讓一次使用者操作的完整路徑 — 從 browser click 到 server 處理到 response rendering — 可以用同一個 trace ID 串起來。<a href="/blog/backend/04-observability/client-side-monitoring/" data-link-title="4.10 Client-side / Synthetic / RUM" data-link-desc="補 server-side 看不到的 user perceived 訊號">4.10 Client-side / Synthetic / RUM</a> 講的是概念和 vendor 定位；本篇走完一個具體場景的實作鏈路。<a href="/blog/monitoring/03-sdk-design/" data-link-title="模組三：SDK 設計模式" data-link-desc="跨平台 SDK 的自動攔截、手動上報、攢批送出、離線 buffer 設計">Monitoring 模組 03 SDK 設計</a> 講的是 client 端怎麼埋點；本篇講 server 端怎麼接收和整合。</p>
<h2 id="完整鏈路">完整鏈路</h2>
<p>以使用者在 web app 點擊「結帳」為例，一次操作產生的觀測鏈路：</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">Browser: user clicks &#34;checkout&#34;
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  → RUM SDK 建立 client span（type: resource / xhr）
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  → HTTP POST /api/checkout + W3C traceparent header
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    → Server middleware 提取 trace context
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    → Server 建立 child span（checkout-handler）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">      → DB query span（order insert）
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">      → Cache span（inventory check）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">      → Queue span（event publish）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    → Server 回 200 + response body
</span></span><span class="line"><span class="ln">10</span><span class="cl">  → Browser 收到 response → resource timing 結束
</span></span><span class="line"><span class="ln">11</span><span class="cl">  → RUM SDK 關閉 client span（記錄 duration + status）
</span></span><span class="line"><span class="ln">12</span><span class="cl">  → 統一 trace waterfall：client span 是 root、server spans 是 children</span></span></code></pre></div><p>鏈路的每一段都需要 trace context 正確傳遞。任何一段斷掉，trace waterfall 就會出現孤立的 span — server 端看到的 trace 跟 client 端看到的 trace 是兩條不相關的紀錄。</p>
<h2 id="trace-context-propagation">Trace context propagation</h2>
<h3 id="w3c-traceparent-header">W3C traceparent header</h3>
<p>W3C Trace Context 是跨 vendor 的標準 propagation 格式。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">traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
</span></span><span class="line"><span class="ln">2</span><span class="cl">              │  │                                │                  │
</span></span><span class="line"><span class="ln">3</span><span class="cl">              │  trace-id (32 hex)                 parent-id (16 hex) flags
</span></span><span class="line"><span class="ln">4</span><span class="cl">              version</span></span></code></pre></div><p>RUM SDK 在發起 XHR / fetch 時把 <code>traceparent</code> 注入 request header。Server 的 trace SDK 從 header 提取 trace-id 和 parent-id，建立 child span。</p>
<h3 id="client-端注入">Client 端注入</h3>
<p>各 RUM SDK 的注入方式：</p>
<table>
  <thead>
      <tr>
          <th>SDK</th>
          <th>注入機制</th>
          <th>配置</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Datadog RUM</td>
          <td>自動 patch XHR / fetch，注入 <code>x-datadog-*</code> + 可選 <code>traceparent</code></td>
          <td><code>allowedTracingUrls</code> 設定允許注入的 domain</td>
      </tr>
      <tr>
          <td>Sentry browser</td>
          <td>自動 patch fetch / XHR，注入 <code>sentry-trace</code> + <code>baggage</code> + 可選 <code>traceparent</code></td>
          <td><code>tracePropagationTargets</code> 設定目標 URL</td>
      </tr>
      <tr>
          <td>OTel browser SDK</td>
          <td>透過 <code>XMLHttpRequestInstrumentation</code> / <code>FetchInstrumentation</code> 注入 <code>traceparent</code></td>
          <td><code>propagateTraceHeaderCorsUrls</code> 設定 CORS 允許的 URL</td>
      </tr>
  </tbody>
</table>
<p>三者的共同模式：只對設定的 domain 注入 trace header。不設定白名單時，header 不會被注入到第三方 API（避免 information leakage）。</p>
<h3 id="server-端提取">Server 端提取</h3>
<p>Server 端的 trace SDK（OTel auto-instrumentation 或 vendor agent）從 incoming request 的 header 提取 trace context：</p>





<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"># OTel Python 範例 — auto-instrumentation 自動處理</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"># 不需要手動提取，middleware 自動讀 traceparent header</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"># 建立的 span 會繼承 client 傳來的 trace-id 和 parent-id</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="c1"># 手動提取（不用 auto-instrumentation 時）</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="kn">from</span> <span class="nn">opentelemetry.propagate</span> <span class="kn">import</span> <span class="n">extract</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">ctx</span> <span class="o">=</span> <span class="n">extract</span><span class="p">(</span><span class="n">carrier</span><span class="o">=</span><span class="n">request</span><span class="o">.</span><span class="n">headers</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 8</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="s2">&#34;checkout-handler&#34;</span><span class="p">,</span> <span class="n">context</span><span class="o">=</span><span class="n">ctx</span><span class="p">):</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="c1"># server logic</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="k">pass</span></span></span></code></pre></div><h3 id="cors-限制">CORS 限制</h3>
<p>跨域請求時，browser 的 CORS preflight 會阻止非標準 header。Server 需要明確允許 trace 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">Access-Control-Allow-Headers: traceparent, tracestate, sentry-trace, baggage</span></span></code></pre></div><p>CORS 是 client-server trace 串接最常見的斷裂原因。Server 沒有回 <code>Access-Control-Allow-Headers: traceparent</code> 時，browser 會 strip 掉 trace header，server 端收到的 request 沒有 trace context，建立的 span 成為新的 root — 跟 client span 斷裂。</p>
<h2 id="跨層-correlation-設計">跨層 correlation 設計</h2>
<h3 id="trace-id-串接">Trace ID 串接</h3>
<p>統一 trace-id 是最基本的 correlation。同一個 trace-id 下的所有 span（client + server）可以在 trace backend 的 waterfall view 裡按時間排列，看到完整的 request 路徑。</p>
<h3 id="session-跟-transaction-的-mapping">Session 跟 transaction 的 mapping</h3>
<p>RUM SDK 的 session（使用者的一次造訪）包含多個 user action，每個 action 可能觸發多個 HTTP request。Mapping 關係：</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">RUM session
</span></span><span class="line"><span class="ln">2</span><span class="cl">  └── user action (click &#34;checkout&#34;)
</span></span><span class="line"><span class="ln">3</span><span class="cl">        ├── HTTP request /api/checkout  →  server transaction (trace)
</span></span><span class="line"><span class="ln">4</span><span class="cl">        ├── HTTP request /api/inventory →  server transaction (trace)
</span></span><span class="line"><span class="ln">5</span><span class="cl">        └── client-side rendering time</span></span></code></pre></div><p>Datadog RUM 和 Sentry 都支援從 session replay 點進去看對應的 server trace。這個 mapping 靠的是 RUM event 裡記錄的 trace-id，跟 server trace backend 裡的同一個 trace-id 做 join。</p>
<h3 id="breadcrumbs-跟-server-log-的時間對齊">Breadcrumbs 跟 server log 的時間對齊</h3>
<p>RUM SDK 收集的 breadcrumbs（使用者操作序列：page view → button click → form submit）跟 server-side log 的 timestamp 需要可比對。時間對齊的前提是 client 和 server 的 clock 差距在可接受範圍（通常 &lt; 1s）。</p>
<p>NTP 同步的 server 端 clock 通常精準。Client 端（browser）依賴使用者裝置的系統時間，可能偏差數秒到數分鐘。RUM SDK 通常會記錄 relative timing（相對於 session 開始的 offset），而非絕對 timestamp，來降低 clock skew 的影響。</p>
<h3 id="error-correlation">Error correlation</h3>
<p>Client-side JS error 跟 server-side 5xx 可能是同一個問題的兩面。Correlation 方式：</p>
<ul>
<li><strong>同一 trace-id</strong>：client error 發生在某個 HTTP request 的 response 處理中，該 request 的 trace-id 跟 server-side 500 的 trace-id 相同 — 直接 correlation</li>
<li><strong>時間窗 + endpoint</strong>：client error 沒有 trace-id（例如 CORS block 導致 request 沒發出），用時間窗 + endpoint 模式做 fuzzy correlation</li>
<li><strong>Server 無異常但 client 報錯</strong>：client-side rendering error（JSON parse failure、type error），server 端看不到 — 需要 RUM 獨立分析</li>
</ul>
<h2 id="evidence-package-整合">Evidence package 整合</h2>
<p>把 client-side 訊號納入 <a href="/blog/backend/04-observability/observability-evidence-package/" data-link-title="4.20 Observability Evidence Package" data-link-desc="把 log、metric、trace、audit 與資料品質限制包成可交接證據">4.20 Observability Evidence Package</a> 時，需要額外記錄：</p>
<table>
  <thead>
      <tr>
          <th>欄位</th>
          <th>Client-side 補充</th>
          <th>為什麼需要</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Source</td>
          <td>標註 &ldquo;RUM&rdquo; 或 &ldquo;Synthetic&rdquo;</td>
          <td>區分 server-side metrics 和 client-side metrics</td>
      </tr>
      <tr>
          <td>Latency</td>
          <td>Client perceived latency（含 DNS + network + server + rendering）</td>
          <td>跟 server-side latency 差異是 network + rendering 時間</td>
      </tr>
      <tr>
          <td>Known gap</td>
          <td>Trace sampling 不一致</td>
          <td>Client 和 server 可能各自取樣，同一個 request 不一定兩邊都有</td>
      </tr>
      <tr>
          <td>Confidence</td>
          <td>Client clock skew 可能影響 timestamp precision</td>
          <td>標注 client timestamp 的精確度限制</td>
      </tr>
  </tbody>
</table>
<p>Client perceived latency 跟 server-side latency 的差異本身就是一個觀測訊號。差異穩定在 50ms 是正常的 network overhead；差異突然從 50ms 跳到 500ms 代表網路或 CDN 出了問題 — 而這個問題 server-side dashboard 完全看不到。</p>
<h2 id="失敗場景判讀">失敗場景判讀</h2>
<table>
  <thead>
      <tr>
          <th>失敗訊號</th>
          <th>判讀</th>
          <th>下一步</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Client span 存在但 server span 缺失</td>
          <td>Trace context header 沒被 propagate — 最常見原因是 CORS block</td>
          <td>檢查 <code>Access-Control-Allow-Headers</code> 是否包含 <code>traceparent</code>；檢查 RUM SDK 的 <code>allowedTracingUrls</code> 設定</td>
      </tr>
      <tr>
          <td>Server 正常但 client perceived latency 高</td>
          <td>網路延遲或 client rendering 慢</td>
          <td>看 RUM 的 resource timing breakdown（DNS / TCP / TLS / TTFB / download / render）</td>
      </tr>
      <tr>
          <td>Client error 但 server 無對應 request</td>
          <td>Request 沒發出 — client-side validation 擋掉或 network offline</td>
          <td>看 RUM breadcrumbs 確認 request 是否有送出；檢查 navigator.onLine 狀態</td>
      </tr>
      <tr>
          <td>Trace sampling 不一致</td>
          <td>Client 取樣到但 server 沒取樣到同一個 request</td>
          <td>統一 sampling decision — 用 head-based sampling（decision 在 trace 起點做、propagate 到下游）</td>
      </tr>
      <tr>
          <td>Client 和 server 的 error count 對不上</td>
          <td>Client 包含 JS rendering error（server 看不到）；server 包含非 user-facing 的背景 job error</td>
          <td>分開看：API error 用 trace correlation 比對、non-API error 各自歸類</td>
      </tr>
  </tbody>
</table>
<h2 id="vendor-整合模式">Vendor 整合模式</h2>
<table>
  <thead>
      <tr>
          <th>組合</th>
          <th>串接方式</th>
          <th>限制</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Datadog RUM + Datadog APM</td>
          <td>原生 — 同一個 Datadog org 裡 client 跟 server trace 自動關聯</td>
          <td>兩邊都要 Datadog plan</td>
      </tr>
      <tr>
          <td>Sentry browser + Sentry server</td>
          <td>原生 — <code>sentry-trace</code> header propagation</td>
          <td>Performance monitoring 需要 Sentry paid plan</td>
      </tr>
      <tr>
          <td>OTel browser SDK + OTel server SDK</td>
          <td>W3C <code>traceparent</code> — vendor-neutral 標準</td>
          <td>Browser SDK 較新、instrumentation 覆蓋度不如 server 端成熟</td>
      </tr>
      <tr>
          <td>混合（Sentry browser + Datadog server）</td>
          <td>手動橋接 — 確保雙方都支援 W3C <code>traceparent</code></td>
          <td>Trace context format 要一致；session-level correlation 需自建</td>
      </tr>
  </tbody>
</table>
<p>同 vendor 組合的串接最自然。跨 vendor 組合只要雙方都支援 W3C Trace Context，trace-level correlation 可以通；但 session-level 的功能（session replay → server trace）需要同 vendor 才有。</p>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/04-observability/client-side-monitoring/" data-link-title="4.10 Client-side / Synthetic / RUM" data-link-desc="補 server-side 看不到的 user perceived 訊號">4.10 Client-side / Synthetic / RUM</a>：概念定位和 vendor 選型</li>
<li><a href="/blog/backend/04-observability/tracing-context/" data-link-title="4.3 tracing 與 context link" data-link-desc="整理 trace id、span 與跨服務 context propagation">4.3 Tracing Context</a>：server-side trace context 設計</li>
<li><a href="/blog/backend/04-observability/checkout-api-evidence-package/" data-link-title="4.22 Checkout API Evidence Package 實作示範" data-link-desc="用 checkout 路徑示範 evidence package 如何交接給 release gate 與 incident decision。">4.22 Checkout API Evidence Package</a>：evidence 整合到 release gate</li>
<li><a href="/blog/backend/04-observability/observability-evidence-package/" data-link-title="4.20 Observability Evidence Package" data-link-desc="把 log、metric、trace、audit 與資料品質限制包成可交接證據">4.20 Observability Evidence Package</a>：evidence 欄位標準</li>
<li><a href="/blog/monitoring/03-sdk-design/" data-link-title="模組三：SDK 設計模式" data-link-desc="跨平台 SDK 的自動攔截、手動上報、攢批送出、離線 buffer 設計">Monitoring 03 SDK 設計</a>：client-side SDK 埋點設計</li>
<li><a href="/blog/monitoring/06-commercial-comparison/" data-link-title="模組六：商業方案對照" data-link-desc="Sentry / Crashlytics / Datadog RUM / Mixpanel — 自架 vs 商業的功能和成本取捨">Monitoring 06 商業方案</a>：Sentry / Datadog RUM 的 client-side 能力比較</li>
<li><a href="/blog/monitoring/telemetry-data-dual-use/" data-link-title="監控資料的雙重用途：行為分析與訊號治理" data-link-desc="同一份 event data 如何同時服務行為分析（funnel / cohort / attribution）和訊號治理（cardinality / cost / signal governance）— 格式交叉、治理衝突與分流架構">監控資料的雙重用途</a>：同一份 event data 如何同時服務行為分析與訊號治理</li>
</ul>
]]></content:encoded></item></channel></rss>