<?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>模組一：監控心智模型 on Tarragon</title><link>https://tarrragon.github.io/blog/monitoring/01-mental-model/</link><description>Recent content in 模組一：監控心智模型 on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 19 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/monitoring/01-mental-model/index.xml" rel="self" type="application/rss+xml"/><item><title>四類事件的完整定義</title><link>https://tarrragon.github.io/blog/monitoring/01-mental-model/four-event-types/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/01-mental-model/four-event-types/</guid><description>&lt;p>監控資料由四類事件構成。每類事件回答不同的問題，觸發時機不同，消費方式不同。分類的目的是讓「我要收集什麼」有結構化的答案，而非在每個功能上各自決定要不要加 log。&lt;/p>
&lt;h2 id="event使用者做了什麼">Event：使用者做了什麼&lt;/h2>
&lt;p>Event 記錄使用者主動發起的操作。按鈕點擊、頁面瀏覽、表單提交、搜尋查詢 — 每個 event 代表使用者的一個意圖表達。&lt;/p>
&lt;p>Event 的觸發時機是使用者操作發生時。程式碼中的位置通常是 UI 事件處理器（onClick、onSubmit、onNavigate）。&lt;/p>
&lt;p>Event 的消費方式：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Debug context&lt;/strong>：問題發生前使用者做了哪些操作。和 error 事件搭配使用，還原問題的操作路徑。&lt;/li>
&lt;li>&lt;strong>行為分析&lt;/strong>：使用者做了哪些操作、操作順序是什麼、在哪一步停止。&lt;a href="https://tarrragon.github.io/blog/monitoring/knowledge-cards/funnel-analysis/" data-link-title="Funnel Analysis" data-link-desc="說明追蹤使用者在多步驟流程中每一步的轉換率和流失率的分析方法">Funnel analysis&lt;/a> 的原料（&lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/" data-link-title="模組八：行為資料的商業利用" data-link-desc="Funnel / Cohort / Attribution / A/B test / 推薦系統 / RFM — 從 debug 工具到商業資產的翻轉">模組八&lt;/a>）。&lt;/li>
&lt;li>&lt;strong>功能使用率&lt;/strong>：哪些功能被頻繁使用、哪些很少被觸發。功能優先順序的決策依據。&lt;/li>
&lt;/ul>
&lt;h2 id="error什麼出了問題">Error：什麼出了問題&lt;/h2>
&lt;p>Error 記錄程式碼執行中的非預期狀態。例外拋出、assertion 失敗、非預期的 API 回應、資源存取失敗。&lt;/p>
&lt;p>Error 的觸發時機是非預期狀態被偵測到時。來源包括：語言層級的 try/catch 捕獲、框架的全域錯誤處理器（Flutter 的 &lt;code>FlutterError.onError&lt;/code>、JavaScript 的 &lt;code>window.onerror&lt;/code>）、自訂的錯誤檢查邏輯。&lt;/p>
&lt;p>Error 的消費方式：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>即時告警&lt;/strong>：特定類型的 error 或 error 數量超過閾值時通知開發者。&lt;/li>
&lt;li>&lt;strong>趨勢分析&lt;/strong>：error 數量隨時間的變化。新版本部署後 error 是否增加。&lt;/li>
&lt;li>&lt;strong>根因分析&lt;/strong>：error 的 stack trace、觸發條件、影響範圍。和 event 搭配還原「使用者做了什麼導致 error」。&lt;/li>
&lt;/ul>
&lt;h2 id="metric系統狀態的數值快照">Metric：系統狀態的數值快照&lt;/h2>
&lt;p>Metric 記錄系統狀態的可量化指標。回應時間、記憶體使用量、佇列長度、連線數、frame rate。&lt;/p>
&lt;p>Metric 的觸發時機是定期取樣或特定事件發生時。定期取樣適合持續變化的指標（記憶體使用量每 30 秒取一次），事件觸發適合離散的測量（每次 API 回應記錄回應時間）。&lt;/p>
&lt;p>Metric 的消費方式：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>效能監控&lt;/strong>：回應時間的 P50 / P95 / P99 分佈。記憶體使用量的趨勢。&lt;/li>
&lt;li>&lt;strong>容量規劃&lt;/strong>：佇列長度接近上限、連線數接近 pool 上限 — 需要擴容的訊號。&lt;/li>
&lt;li>&lt;strong>SLA 追蹤&lt;/strong>：服務可用性、回應時間是否在承諾範圍內。&lt;/li>
&lt;/ul>
&lt;h2 id="lifecycle系統經歷了什麼階段">Lifecycle：系統經歷了什麼階段&lt;/h2>
&lt;p>Lifecycle 記錄系統本身的狀態轉換。App 啟動、前景/背景切換、連線建立/斷開、版本更新、設定變更。&lt;/p>
&lt;p>Lifecycle 的觸發時機是系統狀態轉換發生時。來源包括：app 生命週期回呼（onCreate、onResume、onPause）、連線狀態變化事件、部署和設定變更鉤子。&lt;/p>
&lt;p>Lifecycle 的消費方式：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Session 分析&lt;/strong>：使用者一次使用多久、啟動頻率、前後景切換頻率。&lt;/li>
&lt;li>&lt;strong>環境資訊&lt;/strong>：Error 發生時的系統狀態（app 版本、OS 版本、網路狀態）。&lt;/li>
&lt;li>&lt;strong>連線品質&lt;/strong>：連線建立成功率、斷線頻率、重連次數（&lt;a href="https://tarrragon.github.io/blog/testing/02-client-observability/three-layer-log-design/" data-link-title="三層 log 設計" data-link-desc="連線生命週期 log、protocol 訊息 log、使用者行為 log — 三層各自的職責、詳細程度和啟停控制">testing 模組二 三層 log&lt;/a>）。&lt;/li>
&lt;/ul>
&lt;h2 id="四類事件的區別">四類事件的區別&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>Event&lt;/th>
 &lt;th>Error&lt;/th>
 &lt;th>Metric&lt;/th>
 &lt;th>Lifecycle&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>觸發者&lt;/td>
 &lt;td>使用者操作&lt;/td>
 &lt;td>系統非預期狀態&lt;/td>
 &lt;td>定期取樣或事件觸發&lt;/td>
 &lt;td>系統狀態轉換&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>回答&lt;/td>
 &lt;td>使用者做了什麼&lt;/td>
 &lt;td>什麼出了問題&lt;/td>
 &lt;td>系統現在怎麼樣&lt;/td>
 &lt;td>系統經歷了什麼&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>頻率&lt;/td>
 &lt;td>依使用者行為&lt;/td>
 &lt;td>低（理想狀態）&lt;/td>
 &lt;td>固定間隔或事件驅動&lt;/td>
 &lt;td>低（狀態轉換才有）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>消費&lt;/td>
 &lt;td>行為分析、funnel&lt;/td>
 &lt;td>告警、根因分析&lt;/td>
 &lt;td>效能監控、容量規劃&lt;/td>
 &lt;td>session、環境資訊&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>事件命名規範 → &lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/event-naming-convention/" data-link-title="事件命名規範" data-link-desc="namespace.action 格式的事件命名、命名一致性的工程價值、和商業方案命名慣例的對應">事件命名規範&lt;/a>&lt;/li>
&lt;li>從需求推導收集策略 → &lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/derive-collection-from-requirements/" data-link-title="從需求推導「該收集哪些事件」" data-link-desc="從 debug 需求、行為分析需求、效能需求、合規需求四個方向推導事件收集策略 — 避免「什麼都收」和「什麼都不收」">從需求推導「該收集哪些事件」&lt;/a>&lt;/li>
&lt;li>Event 類事件在商業分析中的用途 → &lt;a href="https://tarrragon.github.io/blog/monitoring/08-business-analytics/" data-link-title="模組八：行為資料的商業利用" data-link-desc="Funnel / Cohort / Attribution / A/B test / 推薦系統 / RFM — 從 debug 工具到商業資產的翻轉">模組八 行為資料的商業利用&lt;/a>&lt;/li>
&lt;li>Log 點的設計方法 → &lt;a href="https://tarrragon.github.io/blog/testing/02-client-observability/" data-link-title="模組二：客戶端可觀測性" data-link-desc="連線生命週期 log、protocol 訊息 log、使用者行為 log — log 設計是功能規格的一部分">testing 模組二 客戶端可觀測性&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>監控資料由四類事件構成。每類事件回答不同的問題，觸發時機不同，消費方式不同。分類的目的是讓「我要收集什麼」有結構化的答案，而非在每個功能上各自決定要不要加 log。</p>
<h2 id="event使用者做了什麼">Event：使用者做了什麼</h2>
<p>Event 記錄使用者主動發起的操作。按鈕點擊、頁面瀏覽、表單提交、搜尋查詢 — 每個 event 代表使用者的一個意圖表達。</p>
<p>Event 的觸發時機是使用者操作發生時。程式碼中的位置通常是 UI 事件處理器（onClick、onSubmit、onNavigate）。</p>
<p>Event 的消費方式：</p>
<ul>
<li><strong>Debug context</strong>：問題發生前使用者做了哪些操作。和 error 事件搭配使用，還原問題的操作路徑。</li>
<li><strong>行為分析</strong>：使用者做了哪些操作、操作順序是什麼、在哪一步停止。<a href="/blog/monitoring/knowledge-cards/funnel-analysis/" data-link-title="Funnel Analysis" data-link-desc="說明追蹤使用者在多步驟流程中每一步的轉換率和流失率的分析方法">Funnel analysis</a> 的原料（<a href="/blog/monitoring/08-business-analytics/" data-link-title="模組八：行為資料的商業利用" data-link-desc="Funnel / Cohort / Attribution / A/B test / 推薦系統 / RFM — 從 debug 工具到商業資產的翻轉">模組八</a>）。</li>
<li><strong>功能使用率</strong>：哪些功能被頻繁使用、哪些很少被觸發。功能優先順序的決策依據。</li>
</ul>
<h2 id="error什麼出了問題">Error：什麼出了問題</h2>
<p>Error 記錄程式碼執行中的非預期狀態。例外拋出、assertion 失敗、非預期的 API 回應、資源存取失敗。</p>
<p>Error 的觸發時機是非預期狀態被偵測到時。來源包括：語言層級的 try/catch 捕獲、框架的全域錯誤處理器（Flutter 的 <code>FlutterError.onError</code>、JavaScript 的 <code>window.onerror</code>）、自訂的錯誤檢查邏輯。</p>
<p>Error 的消費方式：</p>
<ul>
<li><strong>即時告警</strong>：特定類型的 error 或 error 數量超過閾值時通知開發者。</li>
<li><strong>趨勢分析</strong>：error 數量隨時間的變化。新版本部署後 error 是否增加。</li>
<li><strong>根因分析</strong>：error 的 stack trace、觸發條件、影響範圍。和 event 搭配還原「使用者做了什麼導致 error」。</li>
</ul>
<h2 id="metric系統狀態的數值快照">Metric：系統狀態的數值快照</h2>
<p>Metric 記錄系統狀態的可量化指標。回應時間、記憶體使用量、佇列長度、連線數、frame rate。</p>
<p>Metric 的觸發時機是定期取樣或特定事件發生時。定期取樣適合持續變化的指標（記憶體使用量每 30 秒取一次），事件觸發適合離散的測量（每次 API 回應記錄回應時間）。</p>
<p>Metric 的消費方式：</p>
<ul>
<li><strong>效能監控</strong>：回應時間的 P50 / P95 / P99 分佈。記憶體使用量的趨勢。</li>
<li><strong>容量規劃</strong>：佇列長度接近上限、連線數接近 pool 上限 — 需要擴容的訊號。</li>
<li><strong>SLA 追蹤</strong>：服務可用性、回應時間是否在承諾範圍內。</li>
</ul>
<h2 id="lifecycle系統經歷了什麼階段">Lifecycle：系統經歷了什麼階段</h2>
<p>Lifecycle 記錄系統本身的狀態轉換。App 啟動、前景/背景切換、連線建立/斷開、版本更新、設定變更。</p>
<p>Lifecycle 的觸發時機是系統狀態轉換發生時。來源包括：app 生命週期回呼（onCreate、onResume、onPause）、連線狀態變化事件、部署和設定變更鉤子。</p>
<p>Lifecycle 的消費方式：</p>
<ul>
<li><strong>Session 分析</strong>：使用者一次使用多久、啟動頻率、前後景切換頻率。</li>
<li><strong>環境資訊</strong>：Error 發生時的系統狀態（app 版本、OS 版本、網路狀態）。</li>
<li><strong>連線品質</strong>：連線建立成功率、斷線頻率、重連次數（<a href="/blog/testing/02-client-observability/three-layer-log-design/" data-link-title="三層 log 設計" data-link-desc="連線生命週期 log、protocol 訊息 log、使用者行為 log — 三層各自的職責、詳細程度和啟停控制">testing 模組二 三層 log</a>）。</li>
</ul>
<h2 id="四類事件的區別">四類事件的區別</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Event</th>
          <th>Error</th>
          <th>Metric</th>
          <th>Lifecycle</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>觸發者</td>
          <td>使用者操作</td>
          <td>系統非預期狀態</td>
          <td>定期取樣或事件觸發</td>
          <td>系統狀態轉換</td>
      </tr>
      <tr>
          <td>回答</td>
          <td>使用者做了什麼</td>
          <td>什麼出了問題</td>
          <td>系統現在怎麼樣</td>
          <td>系統經歷了什麼</td>
      </tr>
      <tr>
          <td>頻率</td>
          <td>依使用者行為</td>
          <td>低（理想狀態）</td>
          <td>固定間隔或事件驅動</td>
          <td>低（狀態轉換才有）</td>
      </tr>
      <tr>
          <td>消費</td>
          <td>行為分析、funnel</td>
          <td>告警、根因分析</td>
          <td>效能監控、容量規劃</td>
          <td>session、環境資訊</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>事件命名規範 → <a href="/blog/monitoring/01-mental-model/event-naming-convention/" data-link-title="事件命名規範" data-link-desc="namespace.action 格式的事件命名、命名一致性的工程價值、和商業方案命名慣例的對應">事件命名規範</a></li>
<li>從需求推導收集策略 → <a href="/blog/monitoring/01-mental-model/derive-collection-from-requirements/" data-link-title="從需求推導「該收集哪些事件」" data-link-desc="從 debug 需求、行為分析需求、效能需求、合規需求四個方向推導事件收集策略 — 避免「什麼都收」和「什麼都不收」">從需求推導「該收集哪些事件」</a></li>
<li>Event 類事件在商業分析中的用途 → <a href="/blog/monitoring/08-business-analytics/" data-link-title="模組八：行為資料的商業利用" data-link-desc="Funnel / Cohort / Attribution / A/B test / 推薦系統 / RFM — 從 debug 工具到商業資產的翻轉">模組八 行為資料的商業利用</a></li>
<li>Log 點的設計方法 → <a href="/blog/testing/02-client-observability/" data-link-title="模組二：客戶端可觀測性" data-link-desc="連線生命週期 log、protocol 訊息 log、使用者行為 log — log 設計是功能規格的一部分">testing 模組二 客戶端可觀測性</a></li>
</ul>
]]></content:encoded></item><item><title>事件命名規範</title><link>https://tarrragon.github.io/blog/monitoring/01-mental-model/event-naming-convention/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/01-mental-model/event-naming-convention/</guid><description>&lt;p>事件命名的目的是讓事件可以被 grep、過濾和統計。統一的命名規範讓不同時期、不同開發者加入的事件能在同一個查詢框架中使用。&lt;/p>
&lt;h2 id="namespaceaction-格式">namespace.action 格式&lt;/h2>
&lt;p>每個事件名稱由兩部分組成：namespace（事件發生的模組或功能區域）和 action（發生了什麼）。用 &lt;code>.&lt;/code> 分隔。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">terminal.connect.start ← namespace: terminal.connect, action: start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">terminal.connect.done ← namespace: terminal.connect, action: &lt;span class="k">done&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">terminal.input.submit ← namespace: terminal.input, action: submit
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">auth.biometric.success ← namespace: auth.biometric, action: success
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">auth.biometric.fallback ← namespace: auth.biometric, action: fallback
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">enrollment.qr.scan ← namespace: enrollment.qr, action: scan&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="namespace-層級">Namespace 層級&lt;/h3>
&lt;p>Namespace 的層級深度依功能結構而定。兩層通常足夠（&lt;code>terminal.connect&lt;/code>），三層用於需要進一步區分的場景（&lt;code>terminal.connect.ws&lt;/code>）。超過三層通常代表 namespace 設計過細，增加認知成本但不增加分析價值。&lt;/p>
&lt;h3 id="action-命名">Action 命名&lt;/h3>
&lt;p>Action 使用動詞（&lt;code>start&lt;/code>、&lt;code>submit&lt;/code>、&lt;code>scan&lt;/code>）或狀態（&lt;code>success&lt;/code>、&lt;code>failed&lt;/code>、&lt;code>timeout&lt;/code>）。同一組動作用配對的 action 名稱：&lt;code>start&lt;/code> / &lt;code>done&lt;/code>（成對的生命週期）、&lt;code>success&lt;/code> / &lt;code>failed&lt;/code>（結果分支）。&lt;/p>
&lt;p>避免在 action 中重複 namespace 的資訊。&lt;code>terminal.connect.terminal_connected&lt;/code> 中 &lt;code>terminal&lt;/code> 重複了；&lt;code>terminal.connect.done&lt;/code> 更簡潔。&lt;/p>
&lt;h2 id="命名一致性的工程價值">命名一致性的工程價值&lt;/h2>
&lt;h3 id="grep-友好">Grep 友好&lt;/h3>
&lt;p>統一的 namespace 結構讓開發者用 &lt;code>grep &amp;quot;terminal.connect&amp;quot;&lt;/code> 就能找到所有連線相關事件，不需要知道每個事件的完整名稱。&lt;/p>
&lt;h3 id="統計友好">統計友好&lt;/h3>
&lt;p>按 namespace 前綴分群統計。&lt;code>terminal.*&lt;/code> 的事件數量 = terminal 功能的使用頻率；&lt;code>auth.*&lt;/code> 的事件數量 = 認證觸發頻率。層級結構讓統計的粒度可以調整。&lt;/p>
&lt;h3 id="文件友好">文件友好&lt;/h3>
&lt;p>事件清單按 namespace 排列就是一份結構化的功能地圖。新加入的開發者讀事件清單就能理解系統有哪些功能模組。&lt;/p>
&lt;h2 id="和商業方案的命名對應">和商業方案的命名對應&lt;/h2>
&lt;p>不同的商業監控方案有各自的命名慣例。自架方案用 namespace.action 格式，接入商業方案時需要做對應。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>商業方案&lt;/th>
 &lt;th>命名慣例&lt;/th>
 &lt;th>對應方式&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>GA4&lt;/td>
 &lt;td>&lt;code>event_name&lt;/code> + parameters&lt;/td>
 &lt;td>namespace.action → &lt;code>event_name&lt;/code>，細節放 parameters&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Sentry&lt;/td>
 &lt;td>transaction name + spans&lt;/td>
 &lt;td>namespace → transaction，action → span&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Mixpanel&lt;/td>
 &lt;td>event name + properties&lt;/td>
 &lt;td>namespace.action → event name&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Datadog RUM&lt;/td>
 &lt;td>action name + view name&lt;/td>
 &lt;td>action → action name，namespace → view&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>對應時保持一個原則：自架方案的事件名稱是 source of truth，商業方案的名稱是它的映射。在自架方案中改名後，映射層跟著改；不要讓商業方案的命名反過來影響自架的命名結構。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>四類事件的定義 → &lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/four-event-types/" data-link-title="四類事件的完整定義" data-link-desc="Event / Error / Metric / Lifecycle 四類事件各自的語意、觸發時機和典型用途 — 分類是監控體系的統一語言">四類事件的完整定義&lt;/a>&lt;/li>
&lt;li>從需求推導收集策略 → &lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/derive-collection-from-requirements/" data-link-title="從需求推導「該收集哪些事件」" data-link-desc="從 debug 需求、行為分析需求、效能需求、合規需求四個方向推導事件收集策略 — 避免「什麼都收」和「什麼都不收」">從需求推導「該收集哪些事件」&lt;/a>&lt;/li>
&lt;li>商業方案的完整比較 → &lt;a href="https://tarrragon.github.io/blog/monitoring/06-commercial-comparison/" data-link-title="模組六：商業方案對照" data-link-desc="Sentry / Crashlytics / Datadog RUM / Mixpanel — 自架 vs 商業的功能和成本取捨">模組六 商業方案比較&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>事件命名的目的是讓事件可以被 grep、過濾和統計。統一的命名規範讓不同時期、不同開發者加入的事件能在同一個查詢框架中使用。</p>
<h2 id="namespaceaction-格式">namespace.action 格式</h2>
<p>每個事件名稱由兩部分組成：namespace（事件發生的模組或功能區域）和 action（發生了什麼）。用 <code>.</code> 分隔。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">terminal.connect.start      ← namespace: terminal.connect, action: start
</span></span><span class="line"><span class="ln">2</span><span class="cl">terminal.connect.done       ← namespace: terminal.connect, action: <span class="k">done</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">terminal.input.submit       ← namespace: terminal.input, action: submit
</span></span><span class="line"><span class="ln">4</span><span class="cl">auth.biometric.success      ← namespace: auth.biometric, action: success
</span></span><span class="line"><span class="ln">5</span><span class="cl">auth.biometric.fallback     ← namespace: auth.biometric, action: fallback
</span></span><span class="line"><span class="ln">6</span><span class="cl">enrollment.qr.scan          ← namespace: enrollment.qr, action: scan</span></span></code></pre></div><h3 id="namespace-層級">Namespace 層級</h3>
<p>Namespace 的層級深度依功能結構而定。兩層通常足夠（<code>terminal.connect</code>），三層用於需要進一步區分的場景（<code>terminal.connect.ws</code>）。超過三層通常代表 namespace 設計過細，增加認知成本但不增加分析價值。</p>
<h3 id="action-命名">Action 命名</h3>
<p>Action 使用動詞（<code>start</code>、<code>submit</code>、<code>scan</code>）或狀態（<code>success</code>、<code>failed</code>、<code>timeout</code>）。同一組動作用配對的 action 名稱：<code>start</code> / <code>done</code>（成對的生命週期）、<code>success</code> / <code>failed</code>（結果分支）。</p>
<p>避免在 action 中重複 namespace 的資訊。<code>terminal.connect.terminal_connected</code> 中 <code>terminal</code> 重複了；<code>terminal.connect.done</code> 更簡潔。</p>
<h2 id="命名一致性的工程價值">命名一致性的工程價值</h2>
<h3 id="grep-友好">Grep 友好</h3>
<p>統一的 namespace 結構讓開發者用 <code>grep &quot;terminal.connect&quot;</code> 就能找到所有連線相關事件，不需要知道每個事件的完整名稱。</p>
<h3 id="統計友好">統計友好</h3>
<p>按 namespace 前綴分群統計。<code>terminal.*</code> 的事件數量 = terminal 功能的使用頻率；<code>auth.*</code> 的事件數量 = 認證觸發頻率。層級結構讓統計的粒度可以調整。</p>
<h3 id="文件友好">文件友好</h3>
<p>事件清單按 namespace 排列就是一份結構化的功能地圖。新加入的開發者讀事件清單就能理解系統有哪些功能模組。</p>
<h2 id="和商業方案的命名對應">和商業方案的命名對應</h2>
<p>不同的商業監控方案有各自的命名慣例。自架方案用 namespace.action 格式，接入商業方案時需要做對應。</p>
<table>
  <thead>
      <tr>
          <th>商業方案</th>
          <th>命名慣例</th>
          <th>對應方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>GA4</td>
          <td><code>event_name</code> + parameters</td>
          <td>namespace.action → <code>event_name</code>，細節放 parameters</td>
      </tr>
      <tr>
          <td>Sentry</td>
          <td>transaction name + spans</td>
          <td>namespace → transaction，action → span</td>
      </tr>
      <tr>
          <td>Mixpanel</td>
          <td>event name + properties</td>
          <td>namespace.action → event name</td>
      </tr>
      <tr>
          <td>Datadog RUM</td>
          <td>action name + view name</td>
          <td>action → action name，namespace → view</td>
      </tr>
  </tbody>
</table>
<p>對應時保持一個原則：自架方案的事件名稱是 source of truth，商業方案的名稱是它的映射。在自架方案中改名後，映射層跟著改；不要讓商業方案的命名反過來影響自架的命名結構。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>四類事件的定義 → <a href="/blog/monitoring/01-mental-model/four-event-types/" data-link-title="四類事件的完整定義" data-link-desc="Event / Error / Metric / Lifecycle 四類事件各自的語意、觸發時機和典型用途 — 分類是監控體系的統一語言">四類事件的完整定義</a></li>
<li>從需求推導收集策略 → <a href="/blog/monitoring/01-mental-model/derive-collection-from-requirements/" data-link-title="從需求推導「該收集哪些事件」" data-link-desc="從 debug 需求、行為分析需求、效能需求、合規需求四個方向推導事件收集策略 — 避免「什麼都收」和「什麼都不收」">從需求推導「該收集哪些事件」</a></li>
<li>商業方案的完整比較 → <a href="/blog/monitoring/06-commercial-comparison/" data-link-title="模組六：商業方案對照" data-link-desc="Sentry / Crashlytics / Datadog RUM / Mixpanel — 自架 vs 商業的功能和成本取捨">模組六 商業方案比較</a></li>
</ul>
]]></content:encoded></item><item><title>商業方案的事件類型對應</title><link>https://tarrragon.github.io/blog/monitoring/01-mental-model/commercial-event-mapping/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/01-mental-model/commercial-event-mapping/</guid><description>&lt;p>商業監控方案各自有不同的事件分類體系。理解它們的分類邏輯和四類事件（event / error / metric / lifecycle）的對應關係，才能在接入時正確映射自架方案的事件，避免資料遺漏或分類錯誤。&lt;/p>
&lt;h2 id="sentry">Sentry&lt;/h2>
&lt;p>Sentry 的核心概念是 error tracking，但已擴展到 performance monitoring 和 session replay。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>四類事件&lt;/th>
 &lt;th>Sentry 對應&lt;/th>
 &lt;th>說明&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Event&lt;/td>
 &lt;td>Breadcrumb&lt;/td>
 &lt;td>使用者操作記錄在 breadcrumb trail，附加在 error 上&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Error&lt;/td>
 &lt;td>Event（Exception type）&lt;/td>
 &lt;td>Sentry 的核心。自動捕獲 + 手動 captureException&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Metric&lt;/td>
 &lt;td>Transaction + Span&lt;/td>
 &lt;td>Performance monitoring 的度量單位&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Lifecycle&lt;/td>
 &lt;td>Breadcrumb（navigation）&lt;/td>
 &lt;td>app 生命週期記錄為 navigation/system breadcrumb&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Sentry 的設計假設是「error 是主角，其他事件是 error 的 context」。Event 和 lifecycle 都以 breadcrumb 形式附加在 error 報告上，獨立查看的能力有限。Breadcrumb 預設保留最近 100 條且不可獨立查詢 — 它是 error 報告的附件，不是獨立的事件資料庫。Metric 對應的 Transaction + Span 則有獨立的 Performance 頁面可以查看，和 error 是不同的 UI 入口。如果主要需求是行為分析而非 error tracking，Sentry 的 breadcrumb 模型可能不夠用。&lt;/p>
&lt;h2 id="firebase-crashlytics--analytics">Firebase Crashlytics + Analytics&lt;/h2>
&lt;p>Firebase 把 error tracking 和行為分析拆成兩個獨立產品。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>四類事件&lt;/th>
 &lt;th>Firebase 對應&lt;/th>
 &lt;th>說明&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Event&lt;/td>
 &lt;td>Analytics custom event&lt;/td>
 &lt;td>GA4 的 event，有 parameters 附加屬性&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Error&lt;/td>
 &lt;td>Crashlytics exception&lt;/td>
 &lt;td>fatal + non-fatal exception 分開處理&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Metric&lt;/td>
 &lt;td>Analytics event + parameters&lt;/td>
 &lt;td>用 event 的 parameters 記錄數值（無原生 metric）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Lifecycle&lt;/td>
 &lt;td>Analytics auto events&lt;/td>
 &lt;td>screen_view、app_open 等自動收集&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Firebase 的特點是 Crashlytics 和 Analytics 各自獨立運作 — error 資料在 Crashlytics console，行為資料在 Analytics console。Metric 沒有原生支援，只能用 Analytics event 的 parameters 欄位記錄數值（例如 &lt;code>event: 'page_load', parameters: {duration_ms: 320}&lt;/code>），查詢時需要在 BigQuery export 中自行聚合。兩個 console 之間的關聯需要手動（在 Crashlytics 的 custom key 中設定 user ID，再到 Analytics 用同一個 ID 查行為）。&lt;/p>
&lt;h2 id="datadog-rum">Datadog RUM&lt;/h2>
&lt;p>Datadog Real User Monitoring 從全棧 APM 的角度設計 client-side 監控。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>四類事件&lt;/th>
 &lt;th>Datadog RUM 對應&lt;/th>
 &lt;th>說明&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Event&lt;/td>
 &lt;td>Action&lt;/td>
 &lt;td>使用者操作（click、tap、scroll）自動或手動捕獲&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Error&lt;/td>
 &lt;td>Error&lt;/td>
 &lt;td>JS exception、network error、custom error&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Metric&lt;/td>
 &lt;td>Long Task + 自訂&lt;/td>
 &lt;td>長任務自動捕獲，自訂 metric 用 global context&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Lifecycle&lt;/td>
 &lt;td>View&lt;/td>
 &lt;td>頁面/畫面的進入和離開，自動偵測 SPA route 變換&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Datadog RUM 的特點是和 backend APM 的深度整合。Client-side 的 action 可以關聯到 server-side 的 trace，形成從按鈕點擊到 database query 的完整鏈路。自架方案通常做不到這個深度的跨層關聯。&lt;/p></description><content:encoded><![CDATA[<p>商業監控方案各自有不同的事件分類體系。理解它們的分類邏輯和四類事件（event / error / metric / lifecycle）的對應關係，才能在接入時正確映射自架方案的事件，避免資料遺漏或分類錯誤。</p>
<h2 id="sentry">Sentry</h2>
<p>Sentry 的核心概念是 error tracking，但已擴展到 performance monitoring 和 session replay。</p>
<table>
  <thead>
      <tr>
          <th>四類事件</th>
          <th>Sentry 對應</th>
          <th>說明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Event</td>
          <td>Breadcrumb</td>
          <td>使用者操作記錄在 breadcrumb trail，附加在 error 上</td>
      </tr>
      <tr>
          <td>Error</td>
          <td>Event（Exception type）</td>
          <td>Sentry 的核心。自動捕獲 + 手動 captureException</td>
      </tr>
      <tr>
          <td>Metric</td>
          <td>Transaction + Span</td>
          <td>Performance monitoring 的度量單位</td>
      </tr>
      <tr>
          <td>Lifecycle</td>
          <td>Breadcrumb（navigation）</td>
          <td>app 生命週期記錄為 navigation/system breadcrumb</td>
      </tr>
  </tbody>
</table>
<p>Sentry 的設計假設是「error 是主角，其他事件是 error 的 context」。Event 和 lifecycle 都以 breadcrumb 形式附加在 error 報告上，獨立查看的能力有限。Breadcrumb 預設保留最近 100 條且不可獨立查詢 — 它是 error 報告的附件，不是獨立的事件資料庫。Metric 對應的 Transaction + Span 則有獨立的 Performance 頁面可以查看，和 error 是不同的 UI 入口。如果主要需求是行為分析而非 error tracking，Sentry 的 breadcrumb 模型可能不夠用。</p>
<h2 id="firebase-crashlytics--analytics">Firebase Crashlytics + Analytics</h2>
<p>Firebase 把 error tracking 和行為分析拆成兩個獨立產品。</p>
<table>
  <thead>
      <tr>
          <th>四類事件</th>
          <th>Firebase 對應</th>
          <th>說明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Event</td>
          <td>Analytics custom event</td>
          <td>GA4 的 event，有 parameters 附加屬性</td>
      </tr>
      <tr>
          <td>Error</td>
          <td>Crashlytics exception</td>
          <td>fatal + non-fatal exception 分開處理</td>
      </tr>
      <tr>
          <td>Metric</td>
          <td>Analytics event + parameters</td>
          <td>用 event 的 parameters 記錄數值（無原生 metric）</td>
      </tr>
      <tr>
          <td>Lifecycle</td>
          <td>Analytics auto events</td>
          <td>screen_view、app_open 等自動收集</td>
      </tr>
  </tbody>
</table>
<p>Firebase 的特點是 Crashlytics 和 Analytics 各自獨立運作 — error 資料在 Crashlytics console，行為資料在 Analytics console。Metric 沒有原生支援，只能用 Analytics event 的 parameters 欄位記錄數值（例如 <code>event: 'page_load', parameters: {duration_ms: 320}</code>），查詢時需要在 BigQuery export 中自行聚合。兩個 console 之間的關聯需要手動（在 Crashlytics 的 custom key 中設定 user ID，再到 Analytics 用同一個 ID 查行為）。</p>
<h2 id="datadog-rum">Datadog RUM</h2>
<p>Datadog Real User Monitoring 從全棧 APM 的角度設計 client-side 監控。</p>
<table>
  <thead>
      <tr>
          <th>四類事件</th>
          <th>Datadog RUM 對應</th>
          <th>說明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Event</td>
          <td>Action</td>
          <td>使用者操作（click、tap、scroll）自動或手動捕獲</td>
      </tr>
      <tr>
          <td>Error</td>
          <td>Error</td>
          <td>JS exception、network error、custom error</td>
      </tr>
      <tr>
          <td>Metric</td>
          <td>Long Task + 自訂</td>
          <td>長任務自動捕獲，自訂 metric 用 global context</td>
      </tr>
      <tr>
          <td>Lifecycle</td>
          <td>View</td>
          <td>頁面/畫面的進入和離開，自動偵測 SPA route 變換</td>
      </tr>
  </tbody>
</table>
<p>Datadog RUM 的特點是和 backend APM 的深度整合。Client-side 的 action 可以關聯到 server-side 的 trace，形成從按鈕點擊到 database query 的完整鏈路。自架方案通常做不到這個深度的跨層關聯。</p>
<h2 id="接入策略">接入策略</h2>
<p>接入商業方案時的映射原則：</p>
<p><strong>自架事件名稱是 source of truth</strong>。商業方案的事件名稱是自架名稱的映射，不是取代。映射邏輯集中在一個 adapter 層，商業方案更換時只改 adapter。</p>
<p><strong>不要為了配合商業方案改變自架的分類</strong>。Sentry 把 event 記錄為 breadcrumb 不代表自架方案也要把 event 降級成 error 的附屬品。自架的四類分類是語意正確的，商業方案的分類是它自己的產品設計。</p>
<p><strong>同時接入多個方案時做去重</strong>。Error 同時發到 Sentry 和 Crashlytics 會產生重複。在 adapter 層控制「哪類事件發到哪個方案」，避免同一個事件在多個 dashboard 出現。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>四類事件的定義 → <a href="/blog/monitoring/01-mental-model/four-event-types/" data-link-title="四類事件的完整定義" data-link-desc="Event / Error / Metric / Lifecycle 四類事件各自的語意、觸發時機和典型用途 — 分類是監控體系的統一語言">四類事件的完整定義</a></li>
<li>商業方案的深入比較 → <a href="/blog/monitoring/06-commercial-comparison/" data-link-title="模組六：商業方案對照" data-link-desc="Sentry / Crashlytics / Datadog RUM / Mixpanel — 自架 vs 商業的功能和成本取捨">模組六 商業方案比較</a></li>
<li>事件命名規範 → <a href="/blog/monitoring/01-mental-model/event-naming-convention/" data-link-title="事件命名規範" data-link-desc="namespace.action 格式的事件命名、命名一致性的工程價值、和商業方案命名慣例的對應">事件命名規範</a></li>
</ul>
]]></content:encoded></item><item><title>從需求推導「該收集哪些事件」</title><link>https://tarrragon.github.io/blog/monitoring/01-mental-model/derive-collection-from-requirements/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/01-mental-model/derive-collection-from-requirements/</guid><description>&lt;p>事件收集策略的起點是需求，而非技術能力。「能收集什麼」取決於 SDK 和 collector 的實作；「該收集什麼」取決於誰需要這些資料、用來做什麼決策。從需求推導收集策略，避免兩個極端：什麼都收（儲存成本高、隱私風險大、真正重要的事件淹沒在噪音中）和什麼都不收（問題發生時沒有資料可查）。&lt;/p>
&lt;h2 id="四個需求方向">四個需求方向&lt;/h2>
&lt;h3 id="debug-需求問題發生時能定位根因">Debug 需求：問題發生時能定位根因&lt;/h3>
&lt;p>Debug 需求驅動的事件收集目標是「問題發生時，開發者能從事件記錄中重建問題的 context」。&lt;/p>
&lt;p>需要的事件類型：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Error&lt;/strong>：例外、非預期狀態、API 錯誤回應。包含 stack trace、error code、觸發條件。&lt;/li>
&lt;li>&lt;strong>Lifecycle&lt;/strong>：問題發生時的系統狀態 — app 版本、OS 版本、網路狀態、前景/背景。&lt;/li>
&lt;li>&lt;strong>Event（最近操作）&lt;/strong>：問題發生前使用者做了哪些操作。不需要完整的操作歷史，最近 10-20 個操作通常足夠。&lt;/li>
&lt;/ul>
&lt;p>推導方法：列出最近三個月遇到的 debug 困難場景，問「如果當時有哪些事件記錄，debug 時間能從 30 分鐘降到 5 分鐘？」。答案就是 debug 需求驅動的事件清單。&lt;/p>
&lt;p>app_tunnel（透過 WebSocket 連接遠端終端機的 Flutter app）的 T.C4 案例是典型的 debug 需求缺口 — 六個元件中四個零 log，debug 只能靠實機反覆測試。如果在企劃階段就設計了連線生命週期的五步 log，auth token 問題在第一次連線就能從 log 定位（&lt;a href="https://tarrragon.github.io/blog/testing/02-client-observability/" data-link-title="模組二：客戶端可觀測性" data-link-desc="連線生命週期 log、protocol 訊息 log、使用者行為 log — log 設計是功能規格的一部分">testing 模組二&lt;/a>）。&lt;/p>
&lt;p>具體的事件表和查詢場景見 &lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/motivation-to-event-mapping/" data-link-title="動機驅動的事件設計" data-link-desc="Debug / 商業 / 資安 / 效能四個動機各自需要什麼事件 — 從「為什麼收」反推「收什麼」和「什麼階段啟用」">動機驅動的事件設計&lt;/a>。&lt;/p>
&lt;h3 id="行為分析需求使用者如何使用產品">行為分析需求：使用者如何使用產品&lt;/h3>
&lt;p>行為分析需求驅動的事件收集目標是「回答產品決策的問題」。&lt;/p>
&lt;p>需要的事件類型：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Event&lt;/strong>：使用者操作的完整記錄。需要足夠的粒度來回答「使用者在哪一步流失」（&lt;a href="https://tarrragon.github.io/blog/monitoring/knowledge-cards/funnel-analysis/" data-link-title="Funnel Analysis" data-link-desc="說明追蹤使用者在多步驟流程中每一步的轉換率和流失率的分析方法">funnel&lt;/a>）和「不同使用者群體的行為差異」（&lt;a href="https://tarrragon.github.io/blog/monitoring/knowledge-cards/cohort-analysis/" data-link-title="Cohort Analysis" data-link-desc="說明把使用者按共同特徵分群、比較不同群組行為差異的分析方法">cohort&lt;/a>）。&lt;/li>
&lt;li>&lt;strong>Lifecycle&lt;/strong>：session 的開始和結束，用於計算使用時長和 session 頻率。&lt;/li>
&lt;/ul>
&lt;p>推導方法：列出產品團隊最常問的 3-5 個問題（「新功能有多少人用」「註冊流程在哪一步流失最多」「付費使用者和免費使用者的行為差異」），為每個問題列出需要的事件。&lt;/p>
&lt;p>自用工具通常沒有行為分析需求 — 使用者就是開發者本人。這個方向的事件可以跳過。&lt;/p>
&lt;p>具體的事件表和查詢場景見 &lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/motivation-to-event-mapping/" data-link-title="動機驅動的事件設計" data-link-desc="Debug / 商業 / 資安 / 效能四個動機各自需要什麼事件 — 從「為什麼收」反推「收什麼」和「什麼階段啟用」">動機驅動的事件設計&lt;/a>。&lt;/p>
&lt;h3 id="效能需求系統是否在可接受的範圍內運作">效能需求：系統是否在可接受的範圍內運作&lt;/h3>
&lt;p>效能需求驅動的事件收集目標是「發現效能退化和容量瓶頸」。&lt;/p>
&lt;p>需要的事件類型：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Metric&lt;/strong>：回應時間、frame rate、記憶體使用量、佇列長度。定期取樣或事件觸發。&lt;/li>
&lt;/ul>
&lt;p>推導方法：列出使用者會感知到的效能指標（頁面載入時間、動畫流暢度、操作回應延遲），為每個指標定義可接受的範圍和取樣頻率。&lt;/p>
&lt;p>具體的事件表和查詢場景見 &lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/motivation-to-event-mapping/" data-link-title="動機驅動的事件設計" data-link-desc="Debug / 商業 / 資安 / 效能四個動機各自需要什麼事件 — 從「為什麼收」反推「收什麼」和「什麼階段啟用」">動機驅動的事件設計&lt;/a>。&lt;/p>
&lt;h3 id="合規需求法規要求收集或禁止收集什麼">合規需求：法規要求收集或禁止收集什麼&lt;/h3>
&lt;p>合規需求同時驅動「必須收集」和「禁止收集」。&lt;/p>
&lt;p>必須收集：access log（誰在什麼時間存取了什麼資料）、audit trail（誰修改了什麼設定）。&lt;/p>
&lt;p>禁止收集：未經同意的個人識別資訊、兒童資料（COPPA）、健康資料（HIPAA）。&lt;/p>
&lt;p>推導方法：確認適用的法規（GDPR、CCPA、個資法），列出法規要求的最小收集項目和禁止項目。&lt;/p>
&lt;p>具體的事件表和查詢場景見 &lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/motivation-to-event-mapping/" data-link-title="動機驅動的事件設計" data-link-desc="Debug / 商業 / 資安 / 效能四個動機各自需要什麼事件 — 從「為什麼收」反推「收什麼」和「什麼階段啟用」">動機驅動的事件設計&lt;/a>。&lt;/p>
&lt;h2 id="從需求到事件清單的步驟">從需求到事件清單的步驟&lt;/h2>
&lt;ol>
&lt;li>&lt;strong>列出需求方向&lt;/strong>：Debug / 行為分析 / 效能 / 合規，每個方向的消費者是誰（開發者 / 產品團隊 / 維運 / 法務）。&lt;/li>
&lt;li>&lt;strong>每個方向列出問題&lt;/strong>：消費者最常需要回答的 3-5 個問題。&lt;/li>
&lt;li>&lt;strong>每個問題列出需要的事件&lt;/strong>：回答這個問題需要哪些事件類型和哪些屬性。&lt;/li>
&lt;li>&lt;strong>去重和分類&lt;/strong>：不同方向可能需要同一個事件（error 事件同時服務 debug 和效能監控）。去重後按四類事件分類。&lt;/li>
&lt;li>&lt;strong>排優先順序&lt;/strong>：按「缺少這個事件的損失」排序。Debug 需求的 error 事件通常是最高優先。&lt;/li>
&lt;/ol>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>四類事件的定義 → &lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/four-event-types/" data-link-title="四類事件的完整定義" data-link-desc="Event / Error / Metric / Lifecycle 四類事件各自的語意、觸發時機和典型用途 — 分類是監控體系的統一語言">四類事件的完整定義&lt;/a>&lt;/li>
&lt;li>事件的命名和結構化 → &lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/event-naming-convention/" data-link-title="事件命名規範" data-link-desc="namespace.action 格式的事件命名、命名一致性的工程價值、和商業方案命名慣例的對應">事件命名規範&lt;/a>&lt;/li>
&lt;li>收集到的事件怎麼處理 → &lt;a href="https://tarrragon.github.io/blog/monitoring/04-collector/" data-link-title="模組四：Collector 設計" data-link-desc="收 → 驗 → 存 → 查 → 觸發的完整鏈路 — Go 單一 binary、可插拔 Storage Backend、rule engine">模組四 Collector 設計&lt;/a>&lt;/li>
&lt;li>四個方向展開到具體事件名稱級 → &lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/motivation-to-event-mapping/" data-link-title="動機驅動的事件設計" data-link-desc="Debug / 商業 / 資安 / 效能四個動機各自需要什麼事件 — 從「為什麼收」反推「收什麼」和「什麼階段啟用」">動機驅動的事件設計&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>事件收集策略的起點是需求，而非技術能力。「能收集什麼」取決於 SDK 和 collector 的實作；「該收集什麼」取決於誰需要這些資料、用來做什麼決策。從需求推導收集策略，避免兩個極端：什麼都收（儲存成本高、隱私風險大、真正重要的事件淹沒在噪音中）和什麼都不收（問題發生時沒有資料可查）。</p>
<h2 id="四個需求方向">四個需求方向</h2>
<h3 id="debug-需求問題發生時能定位根因">Debug 需求：問題發生時能定位根因</h3>
<p>Debug 需求驅動的事件收集目標是「問題發生時，開發者能從事件記錄中重建問題的 context」。</p>
<p>需要的事件類型：</p>
<ul>
<li><strong>Error</strong>：例外、非預期狀態、API 錯誤回應。包含 stack trace、error code、觸發條件。</li>
<li><strong>Lifecycle</strong>：問題發生時的系統狀態 — app 版本、OS 版本、網路狀態、前景/背景。</li>
<li><strong>Event（最近操作）</strong>：問題發生前使用者做了哪些操作。不需要完整的操作歷史，最近 10-20 個操作通常足夠。</li>
</ul>
<p>推導方法：列出最近三個月遇到的 debug 困難場景，問「如果當時有哪些事件記錄，debug 時間能從 30 分鐘降到 5 分鐘？」。答案就是 debug 需求驅動的事件清單。</p>
<p>app_tunnel（透過 WebSocket 連接遠端終端機的 Flutter app）的 T.C4 案例是典型的 debug 需求缺口 — 六個元件中四個零 log，debug 只能靠實機反覆測試。如果在企劃階段就設計了連線生命週期的五步 log，auth token 問題在第一次連線就能從 log 定位（<a href="/blog/testing/02-client-observability/" data-link-title="模組二：客戶端可觀測性" data-link-desc="連線生命週期 log、protocol 訊息 log、使用者行為 log — log 設計是功能規格的一部分">testing 模組二</a>）。</p>
<p>具體的事件表和查詢場景見 <a href="/blog/monitoring/01-mental-model/motivation-to-event-mapping/" data-link-title="動機驅動的事件設計" data-link-desc="Debug / 商業 / 資安 / 效能四個動機各自需要什麼事件 — 從「為什麼收」反推「收什麼」和「什麼階段啟用」">動機驅動的事件設計</a>。</p>
<h3 id="行為分析需求使用者如何使用產品">行為分析需求：使用者如何使用產品</h3>
<p>行為分析需求驅動的事件收集目標是「回答產品決策的問題」。</p>
<p>需要的事件類型：</p>
<ul>
<li><strong>Event</strong>：使用者操作的完整記錄。需要足夠的粒度來回答「使用者在哪一步流失」（<a href="/blog/monitoring/knowledge-cards/funnel-analysis/" data-link-title="Funnel Analysis" data-link-desc="說明追蹤使用者在多步驟流程中每一步的轉換率和流失率的分析方法">funnel</a>）和「不同使用者群體的行為差異」（<a href="/blog/monitoring/knowledge-cards/cohort-analysis/" data-link-title="Cohort Analysis" data-link-desc="說明把使用者按共同特徵分群、比較不同群組行為差異的分析方法">cohort</a>）。</li>
<li><strong>Lifecycle</strong>：session 的開始和結束，用於計算使用時長和 session 頻率。</li>
</ul>
<p>推導方法：列出產品團隊最常問的 3-5 個問題（「新功能有多少人用」「註冊流程在哪一步流失最多」「付費使用者和免費使用者的行為差異」），為每個問題列出需要的事件。</p>
<p>自用工具通常沒有行為分析需求 — 使用者就是開發者本人。這個方向的事件可以跳過。</p>
<p>具體的事件表和查詢場景見 <a href="/blog/monitoring/01-mental-model/motivation-to-event-mapping/" data-link-title="動機驅動的事件設計" data-link-desc="Debug / 商業 / 資安 / 效能四個動機各自需要什麼事件 — 從「為什麼收」反推「收什麼」和「什麼階段啟用」">動機驅動的事件設計</a>。</p>
<h3 id="效能需求系統是否在可接受的範圍內運作">效能需求：系統是否在可接受的範圍內運作</h3>
<p>效能需求驅動的事件收集目標是「發現效能退化和容量瓶頸」。</p>
<p>需要的事件類型：</p>
<ul>
<li><strong>Metric</strong>：回應時間、frame rate、記憶體使用量、佇列長度。定期取樣或事件觸發。</li>
</ul>
<p>推導方法：列出使用者會感知到的效能指標（頁面載入時間、動畫流暢度、操作回應延遲），為每個指標定義可接受的範圍和取樣頻率。</p>
<p>具體的事件表和查詢場景見 <a href="/blog/monitoring/01-mental-model/motivation-to-event-mapping/" data-link-title="動機驅動的事件設計" data-link-desc="Debug / 商業 / 資安 / 效能四個動機各自需要什麼事件 — 從「為什麼收」反推「收什麼」和「什麼階段啟用」">動機驅動的事件設計</a>。</p>
<h3 id="合規需求法規要求收集或禁止收集什麼">合規需求：法規要求收集或禁止收集什麼</h3>
<p>合規需求同時驅動「必須收集」和「禁止收集」。</p>
<p>必須收集：access log（誰在什麼時間存取了什麼資料）、audit trail（誰修改了什麼設定）。</p>
<p>禁止收集：未經同意的個人識別資訊、兒童資料（COPPA）、健康資料（HIPAA）。</p>
<p>推導方法：確認適用的法規（GDPR、CCPA、個資法），列出法規要求的最小收集項目和禁止項目。</p>
<p>具體的事件表和查詢場景見 <a href="/blog/monitoring/01-mental-model/motivation-to-event-mapping/" data-link-title="動機驅動的事件設計" data-link-desc="Debug / 商業 / 資安 / 效能四個動機各自需要什麼事件 — 從「為什麼收」反推「收什麼」和「什麼階段啟用」">動機驅動的事件設計</a>。</p>
<h2 id="從需求到事件清單的步驟">從需求到事件清單的步驟</h2>
<ol>
<li><strong>列出需求方向</strong>：Debug / 行為分析 / 效能 / 合規，每個方向的消費者是誰（開發者 / 產品團隊 / 維運 / 法務）。</li>
<li><strong>每個方向列出問題</strong>：消費者最常需要回答的 3-5 個問題。</li>
<li><strong>每個問題列出需要的事件</strong>：回答這個問題需要哪些事件類型和哪些屬性。</li>
<li><strong>去重和分類</strong>：不同方向可能需要同一個事件（error 事件同時服務 debug 和效能監控）。去重後按四類事件分類。</li>
<li><strong>排優先順序</strong>：按「缺少這個事件的損失」排序。Debug 需求的 error 事件通常是最高優先。</li>
</ol>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>四類事件的定義 → <a href="/blog/monitoring/01-mental-model/four-event-types/" data-link-title="四類事件的完整定義" data-link-desc="Event / Error / Metric / Lifecycle 四類事件各自的語意、觸發時機和典型用途 — 分類是監控體系的統一語言">四類事件的完整定義</a></li>
<li>事件的命名和結構化 → <a href="/blog/monitoring/01-mental-model/event-naming-convention/" data-link-title="事件命名規範" data-link-desc="namespace.action 格式的事件命名、命名一致性的工程價值、和商業方案命名慣例的對應">事件命名規範</a></li>
<li>收集到的事件怎麼處理 → <a href="/blog/monitoring/04-collector/" data-link-title="模組四：Collector 設計" data-link-desc="收 → 驗 → 存 → 查 → 觸發的完整鏈路 — Go 單一 binary、可插拔 Storage Backend、rule engine">模組四 Collector 設計</a></li>
<li>四個方向展開到具體事件名稱級 → <a href="/blog/monitoring/01-mental-model/motivation-to-event-mapping/" data-link-title="動機驅動的事件設計" data-link-desc="Debug / 商業 / 資安 / 效能四個動機各自需要什麼事件 — 從「為什麼收」反推「收什麼」和「什麼階段啟用」">動機驅動的事件設計</a></li>
</ul>
]]></content:encoded></item><item><title>事件枚舉與補齊檢查</title><link>https://tarrragon.github.io/blog/monitoring/01-mental-model/event-enumeration-method/</link><pubDate>Sat, 20 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/01-mental-model/event-enumeration-method/</guid><description>&lt;p>事件枚舉的目的是為一個服務建立完整的事件清單 — 每個事件有明確的類型、名稱、觸發時機和 data schema。枚舉的方法從操作盤點出發，經過四類補齊檢查，產出可以直接實作 SDK 埋點的事件表。&lt;/p>
&lt;h2 id="從操作盤點推導事件">從操作盤點推導事件&lt;/h2>
&lt;p>每個使用者操作（BDD 操作盤點的產物）至少對應一個 event 類型的事件。操作的失敗路徑對應 error 類型。操作涉及的效能測量對應 metric 類型。操作觸發的系統狀態轉換對應 lifecycle 類型。&lt;/p>
&lt;p>推導鏈：操作 → 四類事件候選 → 命名 → data schema。&lt;/p>
&lt;p>以一個透過 WebSocket 連接遠端終端機的 app 為例，「連線到終端機」這個操作推導出的事件：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>四類&lt;/th>
 &lt;th>事件名稱&lt;/th>
 &lt;th>觸發時機&lt;/th>
 &lt;th>data schema&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>event&lt;/td>
 &lt;td>terminal.connect.start&lt;/td>
 &lt;td>使用者點擊連線按鈕&lt;/td>
 &lt;td>&lt;code>{url, trigger: &amp;quot;manual&amp;quot; | &amp;quot;auto&amp;quot;}&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>event&lt;/td>
 &lt;td>terminal.connect.done&lt;/td>
 &lt;td>連線成功、開始接收 output&lt;/td>
 &lt;td>&lt;code>{url, duration_ms}&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>error&lt;/td>
 &lt;td>terminal.connect.failed&lt;/td>
 &lt;td>連線失敗（逾時、拒絕、認證失敗）&lt;/td>
 &lt;td>&lt;code>{url, error, step}&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>metric&lt;/td>
 &lt;td>terminal.connect.duration&lt;/td>
 &lt;td>連線完成（成功或失敗）&lt;/td>
 &lt;td>&lt;code>{duration_ms, success: bool}&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>lifecycle&lt;/td>
 &lt;td>ws.connected&lt;/td>
 &lt;td>WebSocket 連線狀態轉換&lt;/td>
 &lt;td>&lt;code>{url}&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>lifecycle&lt;/td>
 &lt;td>ws.disconnected&lt;/td>
 &lt;td>WebSocket 斷線&lt;/td>
 &lt;td>&lt;code>{url, reason, code}&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>一個操作推導出六個事件 — 因為這個操作跨越了使用者行為（event）、可能失敗（error）、有效能測量（metric）、涉及系統狀態轉換（lifecycle）四個面向。其中 &lt;code>connect.done&lt;/code> 和 &lt;code>connect.duration&lt;/code> 記錄的是同一事實的兩個面向（見下方邊界案例段），自用場景合併成 &lt;code>connect.done&lt;/code> 帶 &lt;code>duration_ms&lt;/code> 欄位更簡潔。&lt;/p>
&lt;h2 id="四類補齊檢查">四類補齊檢查&lt;/h2>
&lt;p>列完所有操作的事件後，對每個功能區域跑一次四類補齊檢查 — 逐列確認每一類是否都有對應的事件。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>功能區域&lt;/th>
 &lt;th>event&lt;/th>
 &lt;th>error&lt;/th>
 &lt;th>metric&lt;/th>
 &lt;th>lifecycle&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>連線&lt;/td>
 &lt;td>connect.start / connect.done&lt;/td>
 &lt;td>connect.failed&lt;/td>
 &lt;td>connect.duration&lt;/td>
 &lt;td>ws.connected / ws.disconnected&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>認證&lt;/td>
 &lt;td>auth.biometric.attempt&lt;/td>
 &lt;td>auth.biometric.failed&lt;/td>
 &lt;td>auth.duration&lt;/td>
 &lt;td>auth.state_changed&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>輸入&lt;/td>
 &lt;td>input.submit&lt;/td>
 &lt;td>input.parse_error&lt;/td>
 &lt;td>—&lt;/td>
 &lt;td>—&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>配對&lt;/td>
 &lt;td>enrollment.qr.scan / enrollment.done&lt;/td>
 &lt;td>enrollment.failed&lt;/td>
 &lt;td>enrollment.duration&lt;/td>
 &lt;td>—&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>空格是候選遺漏。每個空格問一個問題：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>event 空&lt;/strong>：「這個功能區域有使用者操作嗎？」有 → 補事件；沒有（純系統內部）→ 合理的空格&lt;/li>
&lt;li>&lt;strong>error 空&lt;/strong>：「這個功能區域能失敗嗎？」能 → 補事件；不能失敗的功能極少 → 再想一次&lt;/li>
&lt;li>&lt;strong>metric 空&lt;/strong>：「這個功能區域有值得量測的效能指標嗎？」有 → 補事件；操作瞬間完成且不涉及外部依賴 → 合理的空格&lt;/li>
&lt;li>&lt;strong>lifecycle 空&lt;/strong>：「這個功能區域涉及系統狀態轉換嗎？」有 → 補事件；純資料操作不改系統狀態 → 合理的空格&lt;/li>
&lt;/ul>
&lt;p>上表中「輸入」的 metric 和 lifecycle 空格是合理的 — 文字輸入送出不涉及效能量測和系統狀態轉換。「配對」的 lifecycle 空格也合理 — 配對完成後不改變系統的執行狀態。&lt;/p>
&lt;h2 id="粒度判準">粒度判準&lt;/h2>
&lt;p>事件粒度的判斷用一個 SRP 判準：&lt;strong>一個事件記一個事實&lt;/strong>。&lt;/p>
&lt;h3 id="拆分訊號">拆分訊號&lt;/h3>
&lt;p>一個事件記了兩個獨立的事實 → 拆成兩個事件。&lt;/p>
&lt;p>&lt;code>terminal.connect_and_auth&lt;/code> 同時記錄「連線建立」和「認證通過」。這兩個事實的失敗模式不同（連線失敗是網路問題、認證失敗是帳密問題）、觸發時機不同、消費者不同。拆成 &lt;code>terminal.connect.done&lt;/code> 和 &lt;code>auth.token.sent&lt;/code>。&lt;/p>
&lt;h3 id="合併訊號">合併訊號&lt;/h3>
&lt;p>兩個事件永遠同時觸發且消費者相同 → 合併成一個事件。&lt;/p>
&lt;p>&lt;code>terminal.input.keystroke&lt;/code> 和 &lt;code>terminal.input.keystroke_logged&lt;/code> 永遠同時觸發（每個按鍵一次），data schema 相同。合併成一個 &lt;code>terminal.input.keystroke&lt;/code>。&lt;/p>
&lt;h3 id="邊界案例">邊界案例&lt;/h3>
&lt;p>&lt;code>connect.done&lt;/code> 同時記 event 和 metric（成功事件 + duration）。這是一個事實（連線完成）的兩個面向，可以合併成一個事件帶 &lt;code>duration_ms&lt;/code> 欄位，也可以拆成 event 和 metric 兩筆。判斷依據是查詢需求 — 如果 funnel 分析和效能分析會分開查，拆開讓各自的查詢更簡單；如果都在同一個 dashboard 看，合併減少事件量。&lt;/p>
&lt;h2 id="data-schema-設計">data schema 設計&lt;/h2>
&lt;p>每個事件的 data 欄位回答「發生了什麼的 context」。設計原則：&lt;/p></description><content:encoded><![CDATA[<p>事件枚舉的目的是為一個服務建立完整的事件清單 — 每個事件有明確的類型、名稱、觸發時機和 data schema。枚舉的方法從操作盤點出發，經過四類補齊檢查，產出可以直接實作 SDK 埋點的事件表。</p>
<h2 id="從操作盤點推導事件">從操作盤點推導事件</h2>
<p>每個使用者操作（BDD 操作盤點的產物）至少對應一個 event 類型的事件。操作的失敗路徑對應 error 類型。操作涉及的效能測量對應 metric 類型。操作觸發的系統狀態轉換對應 lifecycle 類型。</p>
<p>推導鏈：操作 → 四類事件候選 → 命名 → data schema。</p>
<p>以一個透過 WebSocket 連接遠端終端機的 app 為例，「連線到終端機」這個操作推導出的事件：</p>
<table>
  <thead>
      <tr>
          <th>四類</th>
          <th>事件名稱</th>
          <th>觸發時機</th>
          <th>data schema</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>event</td>
          <td>terminal.connect.start</td>
          <td>使用者點擊連線按鈕</td>
          <td><code>{url, trigger: &quot;manual&quot; | &quot;auto&quot;}</code></td>
      </tr>
      <tr>
          <td>event</td>
          <td>terminal.connect.done</td>
          <td>連線成功、開始接收 output</td>
          <td><code>{url, duration_ms}</code></td>
      </tr>
      <tr>
          <td>error</td>
          <td>terminal.connect.failed</td>
          <td>連線失敗（逾時、拒絕、認證失敗）</td>
          <td><code>{url, error, step}</code></td>
      </tr>
      <tr>
          <td>metric</td>
          <td>terminal.connect.duration</td>
          <td>連線完成（成功或失敗）</td>
          <td><code>{duration_ms, success: bool}</code></td>
      </tr>
      <tr>
          <td>lifecycle</td>
          <td>ws.connected</td>
          <td>WebSocket 連線狀態轉換</td>
          <td><code>{url}</code></td>
      </tr>
      <tr>
          <td>lifecycle</td>
          <td>ws.disconnected</td>
          <td>WebSocket 斷線</td>
          <td><code>{url, reason, code}</code></td>
      </tr>
  </tbody>
</table>
<p>一個操作推導出六個事件 — 因為這個操作跨越了使用者行為（event）、可能失敗（error）、有效能測量（metric）、涉及系統狀態轉換（lifecycle）四個面向。其中 <code>connect.done</code> 和 <code>connect.duration</code> 記錄的是同一事實的兩個面向（見下方邊界案例段），自用場景合併成 <code>connect.done</code> 帶 <code>duration_ms</code> 欄位更簡潔。</p>
<h2 id="四類補齊檢查">四類補齊檢查</h2>
<p>列完所有操作的事件後，對每個功能區域跑一次四類補齊檢查 — 逐列確認每一類是否都有對應的事件。</p>
<table>
  <thead>
      <tr>
          <th>功能區域</th>
          <th>event</th>
          <th>error</th>
          <th>metric</th>
          <th>lifecycle</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>連線</td>
          <td>connect.start / connect.done</td>
          <td>connect.failed</td>
          <td>connect.duration</td>
          <td>ws.connected / ws.disconnected</td>
      </tr>
      <tr>
          <td>認證</td>
          <td>auth.biometric.attempt</td>
          <td>auth.biometric.failed</td>
          <td>auth.duration</td>
          <td>auth.state_changed</td>
      </tr>
      <tr>
          <td>輸入</td>
          <td>input.submit</td>
          <td>input.parse_error</td>
          <td>—</td>
          <td>—</td>
      </tr>
      <tr>
          <td>配對</td>
          <td>enrollment.qr.scan / enrollment.done</td>
          <td>enrollment.failed</td>
          <td>enrollment.duration</td>
          <td>—</td>
      </tr>
  </tbody>
</table>
<p>空格是候選遺漏。每個空格問一個問題：</p>
<ul>
<li><strong>event 空</strong>：「這個功能區域有使用者操作嗎？」有 → 補事件；沒有（純系統內部）→ 合理的空格</li>
<li><strong>error 空</strong>：「這個功能區域能失敗嗎？」能 → 補事件；不能失敗的功能極少 → 再想一次</li>
<li><strong>metric 空</strong>：「這個功能區域有值得量測的效能指標嗎？」有 → 補事件；操作瞬間完成且不涉及外部依賴 → 合理的空格</li>
<li><strong>lifecycle 空</strong>：「這個功能區域涉及系統狀態轉換嗎？」有 → 補事件；純資料操作不改系統狀態 → 合理的空格</li>
</ul>
<p>上表中「輸入」的 metric 和 lifecycle 空格是合理的 — 文字輸入送出不涉及效能量測和系統狀態轉換。「配對」的 lifecycle 空格也合理 — 配對完成後不改變系統的執行狀態。</p>
<h2 id="粒度判準">粒度判準</h2>
<p>事件粒度的判斷用一個 SRP 判準：<strong>一個事件記一個事實</strong>。</p>
<h3 id="拆分訊號">拆分訊號</h3>
<p>一個事件記了兩個獨立的事實 → 拆成兩個事件。</p>
<p><code>terminal.connect_and_auth</code> 同時記錄「連線建立」和「認證通過」。這兩個事實的失敗模式不同（連線失敗是網路問題、認證失敗是帳密問題）、觸發時機不同、消費者不同。拆成 <code>terminal.connect.done</code> 和 <code>auth.token.sent</code>。</p>
<h3 id="合併訊號">合併訊號</h3>
<p>兩個事件永遠同時觸發且消費者相同 → 合併成一個事件。</p>
<p><code>terminal.input.keystroke</code> 和 <code>terminal.input.keystroke_logged</code> 永遠同時觸發（每個按鍵一次），data schema 相同。合併成一個 <code>terminal.input.keystroke</code>。</p>
<h3 id="邊界案例">邊界案例</h3>
<p><code>connect.done</code> 同時記 event 和 metric（成功事件 + duration）。這是一個事實（連線完成）的兩個面向，可以合併成一個事件帶 <code>duration_ms</code> 欄位，也可以拆成 event 和 metric 兩筆。判斷依據是查詢需求 — 如果 funnel 分析和效能分析會分開查，拆開讓各自的查詢更簡單；如果都在同一個 dashboard 看，合併減少事件量。</p>
<h2 id="data-schema-設計">data schema 設計</h2>
<p>每個事件的 data 欄位回答「發生了什麼的 context」。設計原則：</p>
<p><strong>帶足 debug context</strong>：error 事件的 data 至少包含 error message、發生的步驟、當時的關鍵狀態值。看到這筆 error 事件時、開發者不需要再去查其他來源就能判斷問題方向。</p>
<p><strong>避免過度收集</strong>：data 只帶回答具體問題需要的欄位。<code>terminal.connect.start</code> 帶 URL 和觸發方式就夠了；不需要帶使用者的全部設定。</p>
<p><strong>敏感欄位標記 redaction</strong>：URL 可能含 IP、error message 可能含路徑中的使用者名稱。在事件設計階段標記需要 <a href="/blog/monitoring/knowledge-cards/redaction/" data-link-title="Redaction" data-link-desc="說明在事件資料離開 client 之前把敏感欄位的值替換成遮罩或移除的機制">redaction</a> 的欄位，SDK 實作時自動處理。</p>
<h2 id="事件表的產出格式">事件表的產出格式</h2>
<p>完整的事件表每列七欄：</p>
<table>
  <thead>
      <tr>
          <th>事件名稱</th>
          <th>類型</th>
          <th>觸發時機</th>
          <th>data schema</th>
          <th>redaction 欄位</th>
          <th>保留層級</th>
          <th>備註</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>terminal.connect.start</td>
          <td>event</td>
          <td>使用者點擊連線</td>
          <td><code>{url, trigger}</code></td>
          <td>url</td>
          <td>原始 7d</td>
          <td>funnel 第一步</td>
      </tr>
  </tbody>
</table>
<p>保留層級欄對應分層保留策略 — 哪些事件需要保留原始逐筆資料（debug 用）、哪些只需要聚合摘要（趨勢用）。</p>
<p>事件表是 SDK 埋點的 spec — 開發者照表實作，code review 時逐行勾選。和<a href="/blog/testing/02-client-observability/log-point-in-spec/" data-link-title="功能規格中的 log 點定義方法" data-link-desc="把 log 點設計從 debug 階段前移到功能規格階段 — 每個功能的規格文件新增可觀測性欄位，列出啟動 / 步驟 / 錯誤 / 完成四類 log 點">功能規格中的 log 點定義</a>互補 — log 點是開發期的 debug 設計，事件表是監控期的收集設計。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>四類事件的定義 → <a href="/blog/monitoring/01-mental-model/four-event-types/" data-link-title="四類事件的完整定義" data-link-desc="Event / Error / Metric / Lifecycle 四類事件各自的語意、觸發時機和典型用途 — 分類是監控體系的統一語言">四類事件的完整定義</a></li>
<li>事件命名規範 → <a href="/blog/monitoring/01-mental-model/event-naming-convention/" data-link-title="事件命名規範" data-link-desc="namespace.action 格式的事件命名、命名一致性的工程價值、和商業方案命名慣例的對應">事件命名規範</a></li>
<li>行為事件的 funnel 設計 → <a href="/blog/monitoring/08-business-analytics/behavior-event-design/" data-link-title="行為事件設計" data-link-desc="事件命名規範、屬性設計、funnel 定義 — 行為分析的品質取決於事件設計的品質">行為事件設計</a></li>
<li>事件 schema 的欄位定義 → <a href="/blog/monitoring/02-log-schema/event-schema-fields/" data-link-title="event.schema.json 完整欄位解說" data-link-desc="監控事件的 JSON Schema 定義 — 每個欄位的語意、必填/選填、資料型別和設計理由">event.schema.json 完整欄位解說</a></li>
<li>動機驅動的具體事件對應 → <a href="/blog/monitoring/01-mental-model/motivation-to-event-mapping/" data-link-title="動機驅動的事件設計" data-link-desc="Debug / 商業 / 資安 / 效能四個動機各自需要什麼事件 — 從「為什麼收」反推「收什麼」和「什麼階段啟用」">動機驅動的事件設計</a></li>
</ul>
]]></content:encoded></item><item><title>動機驅動的事件設計</title><link>https://tarrragon.github.io/blog/monitoring/01-mental-model/motivation-to-event-mapping/</link><pubDate>Sat, 20 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/01-mental-model/motivation-to-event-mapping/</guid><description>&lt;p>事件設計是三維結構：動機（為什麼收）決定需要什麼事件、感測器（怎麼收）決定在前端哪裡埋點、生命週期（什麼時候收）決定各事件在哪個產品階段啟用。本章展開&lt;a href="https://tarrragon.github.io/blog/monitoring/01-mental-model/derive-collection-from-requirements/" data-link-title="從需求推導「該收集哪些事件」" data-link-desc="從 debug 需求、行為分析需求、效能需求、合規需求四個方向推導事件收集策略 — 避免「什麼都收」和「什麼都不收」">從需求推導收集策略&lt;/a>的四個方向到具體事件名稱級。從動機出發反推事件清單，比從技術能力出發（「SDK 能收什麼就收什麼」）更精準 — 每個事件都能回指一個具體的消費場景。&lt;/p>
&lt;h2 id="debug-動機">Debug 動機&lt;/h2>
&lt;p>Debug 動機驅動的事件收集目標是「問題發生時、開發者能從事件記錄中重建 context 並定位根因」。&lt;/p>
&lt;h3 id="要偵測的行為">要偵測的行為&lt;/h3>
&lt;ul>
&lt;li>多步驟流程的每一步完成或失敗（連線 → 認證 → 資料交換）&lt;/li>
&lt;li>系統狀態轉換（前景/背景、連線/斷線、登入/登出）&lt;/li>
&lt;li>非預期例外（uncaught exception、network error、timeout）&lt;/li>
&lt;li>使用者最近的操作序列（問題發生前做了什麼）&lt;/li>
&lt;/ul>
&lt;h3 id="事件表">事件表&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>事件名稱&lt;/th>
 &lt;th>類型&lt;/th>
 &lt;th>觸發時機&lt;/th>
 &lt;th>data schema 重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>{feature}.step.done&lt;/td>
 &lt;td>lifecycle&lt;/td>
 &lt;td>流程步驟完成&lt;/td>
 &lt;td>step_name, duration_ms&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>{feature}.step.failed&lt;/td>
 &lt;td>error&lt;/td>
 &lt;td>流程步驟失敗&lt;/td>
 &lt;td>step_name, error, context&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>app.exception&lt;/td>
 &lt;td>error&lt;/td>
 &lt;td>uncaught exception&lt;/td>
 &lt;td>message, stack_trace, component&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>ws.connected / ws.disconnected&lt;/td>
 &lt;td>lifecycle&lt;/td>
 &lt;td>連線狀態變化&lt;/td>
 &lt;td>url, reason, code&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>app.foreground / app.background&lt;/td>
 &lt;td>lifecycle&lt;/td>
 &lt;td>app 前後景切換&lt;/td>
 &lt;td>duration_in_background&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>{action}.completed&lt;/td>
 &lt;td>event&lt;/td>
 &lt;td>使用者完成操作&lt;/td>
 &lt;td>action_detail&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="查詢場景">查詢場景&lt;/h3>
&lt;p>&lt;strong>Session 回放&lt;/strong>：按 session_id 過濾、按時間排序，還原「使用者做了什麼 → 系統發生了什麼 → 問題在哪裡出現」。&lt;/p>
&lt;p>&lt;strong>Error 根因定位&lt;/strong>：按 error name GROUP BY，找出最常出現的錯誤。單筆 error 的 stack_trace + 同 session 的 lifecycle 事件組合，判斷失敗發生在流程的哪一步。&lt;/p>
&lt;p>&lt;strong>最近 N 個操作&lt;/strong>：error 發生前的 10-20 個 event/lifecycle 事件，等同 Sentry 的 breadcrumb trail。&lt;/p>
&lt;h3 id="生命週期階段">生命週期階段&lt;/h3>
&lt;p>開發期起全開。Debug 事件是最早需要的 — 實機測試階段就依賴這些事件定位問題。error 類和 lifecycle 類不做取樣（量低且每筆都可能是線索）。&lt;/p>
&lt;h2 id="商業動機">商業動機&lt;/h2>
&lt;p>商業動機驅動的事件收集目標是「回答產品決策的問題 — 使用者在哪裡流失、不同群組行為有什麼差異、哪些功能被使用」。&lt;/p>
&lt;h3 id="要偵測的行為-1">要偵測的行為&lt;/h3>
&lt;ul>
&lt;li>漏斗步驟完成（註冊 → 啟用 → 付費 → 續約的每一步）&lt;/li>
&lt;li>功能使用頻率（哪些功能被頻繁使用、哪些從未被觸發）&lt;/li>
&lt;li>Session 長度和頻率（使用者多常用、每次用多久）&lt;/li>
&lt;li>關鍵轉換事件（首次付費、邀請好友、升級方案）&lt;/li>
&lt;/ul>
&lt;h3 id="事件表-1">事件表&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>事件名稱&lt;/th>
 &lt;th>類型&lt;/th>
 &lt;th>觸發時機&lt;/th>
 &lt;th>data schema 重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>funnel.{name}.step_N&lt;/td>
 &lt;td>event&lt;/td>
 &lt;td>漏斗步驟完成&lt;/td>
 &lt;td>step_name, funnel_name&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>feature.{name}.used&lt;/td>
 &lt;td>event&lt;/td>
 &lt;td>使用者使用特定功能&lt;/td>
 &lt;td>feature_name, context&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>session.start / session.end&lt;/td>
 &lt;td>lifecycle&lt;/td>
 &lt;td>session 邊界&lt;/td>
 &lt;td>session_duration&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>conversion.{type}&lt;/td>
 &lt;td>event&lt;/td>
 &lt;td>關鍵轉換&lt;/td>
 &lt;td>conversion_type, value&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="查詢場景-1">查詢場景&lt;/h3>
&lt;p>&lt;strong>Funnel 轉換率&lt;/strong>：每步的完成數 / 上一步的完成數。SQLite 層做每步計數，PostgreSQL 層做 session 級 JOIN 的精確轉換率（見 &lt;a href="https://tarrragon.github.io/blog/monitoring/04-collector/feature-tier-boundary/" data-link-title="功能分層與 Backend 選擇" data-link-desc="SQLite 層和 PostgreSQL 層各自承載哪些功能 — 分界線是查詢模式而非資料量、觸發升級的是功能需求而非規模成長">功能分層與 Backend 選擇&lt;/a>）。&lt;/p>
&lt;p>&lt;strong>Cohort 留存&lt;/strong>：按「首次使用週」分群，計算每週的回訪率。需要 session.start 事件 + 使用者首次出現的時間戳。&lt;/p>
&lt;p>&lt;strong>功能使用率&lt;/strong>：feature.*.used 事件按 name GROUP BY COUNT，排序找出最常/最少使用的功能。&lt;/p>
&lt;h3 id="生命週期階段-1">生命週期階段&lt;/h3>
&lt;p>上線後啟用。開發期不需要商業事件（沒有真實使用者）。測試期可以用模擬流量驗證 funnel 事件的觸發正確性，但不做分析。&lt;/p>
&lt;h2 id="資安動機">資安動機&lt;/h2>
&lt;p>資安動機驅動的事件收集目標是「偵測非預期的存取模式、追蹤敏感操作、提供事後稽核的 audit trail」。&lt;/p></description><content:encoded><![CDATA[<p>事件設計是三維結構：動機（為什麼收）決定需要什麼事件、感測器（怎麼收）決定在前端哪裡埋點、生命週期（什麼時候收）決定各事件在哪個產品階段啟用。本章展開<a href="/blog/monitoring/01-mental-model/derive-collection-from-requirements/" data-link-title="從需求推導「該收集哪些事件」" data-link-desc="從 debug 需求、行為分析需求、效能需求、合規需求四個方向推導事件收集策略 — 避免「什麼都收」和「什麼都不收」">從需求推導收集策略</a>的四個方向到具體事件名稱級。從動機出發反推事件清單，比從技術能力出發（「SDK 能收什麼就收什麼」）更精準 — 每個事件都能回指一個具體的消費場景。</p>
<h2 id="debug-動機">Debug 動機</h2>
<p>Debug 動機驅動的事件收集目標是「問題發生時、開發者能從事件記錄中重建 context 並定位根因」。</p>
<h3 id="要偵測的行為">要偵測的行為</h3>
<ul>
<li>多步驟流程的每一步完成或失敗（連線 → 認證 → 資料交換）</li>
<li>系統狀態轉換（前景/背景、連線/斷線、登入/登出）</li>
<li>非預期例外（uncaught exception、network error、timeout）</li>
<li>使用者最近的操作序列（問題發生前做了什麼）</li>
</ul>
<h3 id="事件表">事件表</h3>
<table>
  <thead>
      <tr>
          <th>事件名稱</th>
          <th>類型</th>
          <th>觸發時機</th>
          <th>data schema 重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>{feature}.step.done</td>
          <td>lifecycle</td>
          <td>流程步驟完成</td>
          <td>step_name, duration_ms</td>
      </tr>
      <tr>
          <td>{feature}.step.failed</td>
          <td>error</td>
          <td>流程步驟失敗</td>
          <td>step_name, error, context</td>
      </tr>
      <tr>
          <td>app.exception</td>
          <td>error</td>
          <td>uncaught exception</td>
          <td>message, stack_trace, component</td>
      </tr>
      <tr>
          <td>ws.connected / ws.disconnected</td>
          <td>lifecycle</td>
          <td>連線狀態變化</td>
          <td>url, reason, code</td>
      </tr>
      <tr>
          <td>app.foreground / app.background</td>
          <td>lifecycle</td>
          <td>app 前後景切換</td>
          <td>duration_in_background</td>
      </tr>
      <tr>
          <td>{action}.completed</td>
          <td>event</td>
          <td>使用者完成操作</td>
          <td>action_detail</td>
      </tr>
  </tbody>
</table>
<h3 id="查詢場景">查詢場景</h3>
<p><strong>Session 回放</strong>：按 session_id 過濾、按時間排序，還原「使用者做了什麼 → 系統發生了什麼 → 問題在哪裡出現」。</p>
<p><strong>Error 根因定位</strong>：按 error name GROUP BY，找出最常出現的錯誤。單筆 error 的 stack_trace + 同 session 的 lifecycle 事件組合，判斷失敗發生在流程的哪一步。</p>
<p><strong>最近 N 個操作</strong>：error 發生前的 10-20 個 event/lifecycle 事件，等同 Sentry 的 breadcrumb trail。</p>
<h3 id="生命週期階段">生命週期階段</h3>
<p>開發期起全開。Debug 事件是最早需要的 — 實機測試階段就依賴這些事件定位問題。error 類和 lifecycle 類不做取樣（量低且每筆都可能是線索）。</p>
<h2 id="商業動機">商業動機</h2>
<p>商業動機驅動的事件收集目標是「回答產品決策的問題 — 使用者在哪裡流失、不同群組行為有什麼差異、哪些功能被使用」。</p>
<h3 id="要偵測的行為-1">要偵測的行為</h3>
<ul>
<li>漏斗步驟完成（註冊 → 啟用 → 付費 → 續約的每一步）</li>
<li>功能使用頻率（哪些功能被頻繁使用、哪些從未被觸發）</li>
<li>Session 長度和頻率（使用者多常用、每次用多久）</li>
<li>關鍵轉換事件（首次付費、邀請好友、升級方案）</li>
</ul>
<h3 id="事件表-1">事件表</h3>
<table>
  <thead>
      <tr>
          <th>事件名稱</th>
          <th>類型</th>
          <th>觸發時機</th>
          <th>data schema 重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>funnel.{name}.step_N</td>
          <td>event</td>
          <td>漏斗步驟完成</td>
          <td>step_name, funnel_name</td>
      </tr>
      <tr>
          <td>feature.{name}.used</td>
          <td>event</td>
          <td>使用者使用特定功能</td>
          <td>feature_name, context</td>
      </tr>
      <tr>
          <td>session.start / session.end</td>
          <td>lifecycle</td>
          <td>session 邊界</td>
          <td>session_duration</td>
      </tr>
      <tr>
          <td>conversion.{type}</td>
          <td>event</td>
          <td>關鍵轉換</td>
          <td>conversion_type, value</td>
      </tr>
  </tbody>
</table>
<h3 id="查詢場景-1">查詢場景</h3>
<p><strong>Funnel 轉換率</strong>：每步的完成數 / 上一步的完成數。SQLite 層做每步計數，PostgreSQL 層做 session 級 JOIN 的精確轉換率（見 <a href="/blog/monitoring/04-collector/feature-tier-boundary/" data-link-title="功能分層與 Backend 選擇" data-link-desc="SQLite 層和 PostgreSQL 層各自承載哪些功能 — 分界線是查詢模式而非資料量、觸發升級的是功能需求而非規模成長">功能分層與 Backend 選擇</a>）。</p>
<p><strong>Cohort 留存</strong>：按「首次使用週」分群，計算每週的回訪率。需要 session.start 事件 + 使用者首次出現的時間戳。</p>
<p><strong>功能使用率</strong>：feature.*.used 事件按 name GROUP BY COUNT，排序找出最常/最少使用的功能。</p>
<h3 id="生命週期階段-1">生命週期階段</h3>
<p>上線後啟用。開發期不需要商業事件（沒有真實使用者）。測試期可以用模擬流量驗證 funnel 事件的觸發正確性，但不做分析。</p>
<h2 id="資安動機">資安動機</h2>
<p>資安動機驅動的事件收集目標是「偵測非預期的存取模式、追蹤敏感操作、提供事後稽核的 audit trail」。</p>
<h3 id="要偵測的行為-2">要偵測的行為</h3>
<ul>
<li>認證失敗（密碼錯誤、biometric 失敗、token 過期）</li>
<li>權限越界嘗試（嘗試存取非自己的資源、呼叫無權限的 API）</li>
<li>敏感資料存取（查看個資、匯出資料、修改權限設定）</li>
<li>異常存取模式（短時間大量請求、非常規時段存取、來源 IP 變化）</li>
</ul>
<h3 id="事件表-2">事件表</h3>
<table>
  <thead>
      <tr>
          <th>事件名稱</th>
          <th>類型</th>
          <th>觸發時機</th>
          <th>data schema 重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>auth.{method}.failed</td>
          <td>error</td>
          <td>認證失敗</td>
          <td>method, failure_reason, attempt_count</td>
      </tr>
      <tr>
          <td>auth.{method}.success</td>
          <td>event</td>
          <td>認證成功（語意上是系統回呼、歸為 event 是業界慣例）</td>
          <td>method, duration_ms</td>
      </tr>
      <tr>
          <td>authz.denied</td>
          <td>error</td>
          <td>權限檢查拒絕</td>
          <td>resource, action, role</td>
      </tr>
      <tr>
          <td>sensitive.accessed</td>
          <td>event</td>
          <td>敏感資料被存取</td>
          <td>resource_type, accessor_role</td>
      </tr>
      <tr>
          <td>sensitive.exported</td>
          <td>event</td>
          <td>資料被匯出</td>
          <td>export_format, record_count</td>
      </tr>
      <tr>
          <td>admin.setting.changed</td>
          <td>event</td>
          <td>管理設定變更</td>
          <td>setting_key, old_value_hash, new_value_hash</td>
      </tr>
  </tbody>
</table>
<h3 id="查詢場景-2">查詢場景</h3>
<p><strong>認證失敗監控</strong>：auth.*.failed 事件的 count by session_id，短時間內同一 session 多次失敗 → 暴力破解嫌疑。Rule engine 設閾值告警。</p>
<p><strong>Audit trail</strong>：sensitive.* 和 admin.* 事件按時間排列，回答「誰在什麼時候存取/修改了什麼」。合規審計的必要紀錄。</p>
<p><strong>異常 pattern 偵測</strong>：auth 成功後的操作事件頻率和模式分析。正常使用者每 session 操作 10-50 次；自動化腳本可能操作數千次。</p>
<h3 id="生命週期階段-2">生命週期階段</h3>
<p>開發期起全開。安全事件不能延後 — 「先不收安全事件、上線後再加」等於安全審計的空白期。認證相關事件是 auto-intercept 的一部分（見 <a href="/blog/monitoring/03-sdk-design/auto-intercept/" data-link-title="自動攔截機制" data-link-desc="JS window.onerror / Flutter FlutterError.onError / Python sys.excepthook — 各平台攔截未捕獲例外的機制和限制">自動攔截機制</a>），不需要手動埋點。</p>
<h3 id="和-redaction-的關係">和 redaction 的關係</h3>
<p>資安事件本身可能包含敏感資訊（失敗的密碼、被存取的個資欄位名稱）。事件的 data schema 設計時標記需要 <a href="/blog/monitoring/knowledge-cards/redaction/" data-link-title="Redaction" data-link-desc="說明在事件資料離開 client 之前把敏感欄位的值替換成遮罩或移除的機制">redaction</a> 的欄位 — auth.failed 記錄失敗原因但不記錄輸入的密碼、sensitive.accessed 記錄資源類型但不記錄資源內容。</p>
<h2 id="效能動機">效能動機</h2>
<p>效能動機驅動的事件收集目標是「發現效能退化趨勢、定位效能瓶頸、為容量規劃提供數據」。</p>
<h3 id="要偵測的行為-3">要偵測的行為</h3>
<ul>
<li>操作回應時間（API 呼叫、頁面載入、動畫轉場）</li>
<li>渲染效能（frame rate、長任務、佈局重排）</li>
<li>資源使用（記憶體、CPU、網路流量）</li>
<li>外部依賴延遲（第三方 API、CDN、資料庫查詢）</li>
</ul>
<h3 id="事件表-3">事件表</h3>
<table>
  <thead>
      <tr>
          <th>事件名稱</th>
          <th>類型</th>
          <th>觸發時機</th>
          <th>data schema 重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>{operation}.duration</td>
          <td>metric</td>
          <td>操作完成</td>
          <td>duration_ms, operation_name</td>
      </tr>
      <tr>
          <td>render.frame_drop</td>
          <td>metric</td>
          <td>掉幀偵測</td>
          <td>dropped_frames, total_frames</td>
      </tr>
      <tr>
          <td>resource.memory</td>
          <td>metric</td>
          <td>定期取樣（30s）</td>
          <td>heap_used, heap_total</td>
      </tr>
      <tr>
          <td>dependency.{name}.latency</td>
          <td>metric</td>
          <td>外部呼叫完成</td>
          <td>dependency_name, latency_ms, status</td>
      </tr>
      <tr>
          <td>web.vitals</td>
          <td>metric</td>
          <td>Web 頁面載入</td>
          <td>lcp_ms, fid_ms, cls_score</td>
      </tr>
  </tbody>
</table>
<h3 id="查詢場景-3">查詢場景</h3>
<p><strong>P95 趨勢</strong>：{operation}.duration 事件按天聚合、計算 percentile_cont(0.95)，觀察回應時間是否隨版本增加。</p>
<p><strong>容量規劃</strong>：resource.memory 事件的趨勢圖，判斷記憶體是否隨使用時間穩定增長（memory leak 訊號）。</p>
<p><strong>依賴健康度</strong>：dependency.*.latency 事件按 dependency_name GROUP BY，比較各依賴的平均延遲和失敗率。</p>
<h3 id="生命週期階段-3">生命週期階段</h3>
<p>測試期起啟用。開發期不需要效能事件（本地環境的效能數據不代表 production）。測試期啟用用於建立效能 baseline。上線後持續收集用於趨勢監控。</p>
<p>效能事件量通常最大（每 30 秒一筆 resource.memory × 活躍使用者數），取樣率需要控制 — 自用場景全收、商業產品取樣 10-50%（見 <a href="/blog/monitoring/03-sdk-design/frontend-sensor-design/" data-link-title="前端感測器設計" data-link-desc="什麼行為值得埋感測器、每類感測器的實作方式、取樣策略和效能影響 — 和 auto-intercept 的被動攔截互補">前端感測器設計</a> 的取樣策略段）。</p>
<h2 id="ab-測試動機">A/B 測試動機</h2>
<p>A/B 測試動機驅動的事件是商業動機的延伸 — 實驗期間收集實驗分組和轉換事件，實驗結束後關閉。</p>
<h3 id="事件表-4">事件表</h3>
<table>
  <thead>
      <tr>
          <th>事件名稱</th>
          <th>類型</th>
          <th>觸發時機</th>
          <th>data schema 重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>experiment.{name}.assigned</td>
          <td>event</td>
          <td>使用者被分配到實驗組</td>
          <td>experiment_name, variant</td>
      </tr>
      <tr>
          <td>experiment.{name}.converted</td>
          <td>event</td>
          <td>使用者完成轉換目標</td>
          <td>experiment_name, variant, conversion_type</td>
      </tr>
  </tbody>
</table>
<h3 id="生命週期階段-4">生命週期階段</h3>
<p>實驗期間啟用，實驗結束後關閉（從 SDK config 或 feature flag 移除）。實驗事件的保留期限跟著實驗週期走 — 實驗結束 + 分析完成後可清除。A/B test 的統計分析見 <a href="/blog/monitoring/08-business-analytics/ab-test-statistics/" data-link-title="A/B Test 的統計基礎" data-link-desc="假設檢定、樣本量計算、多重比較校正 — A/B test 不只是「比較兩個數字」，統計方法決定結論是否可靠">A/B test 的統計基礎</a>。</p>
<h2 id="完整對照總表">完整對照總表</h2>
<table>
  <thead>
      <tr>
          <th>動機</th>
          <th>要偵測的行為</th>
          <th>事件名稱模式</th>
          <th>感測器類型</th>
          <th>生命週期啟用</th>
          <th>查詢模式</th>
          <th>保留層級</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Debug</td>
          <td>流程步驟完成/失敗</td>
          <td>{feature}.step.*</td>
          <td>auto-intercept</td>
          <td>開發期起</td>
          <td>session 回放</td>
          <td>原始 7d</td>
      </tr>
      <tr>
          <td>Debug</td>
          <td>例外拋出</td>
          <td>app.exception</td>
          <td>auto-intercept</td>
          <td>開發期起</td>
          <td>error GROUP BY</td>
          <td>原始 30d</td>
      </tr>
      <tr>
          <td>Debug</td>
          <td>連線狀態</td>
          <td>ws.connected/disconnected</td>
          <td>auto-intercept</td>
          <td>開發期起</td>
          <td>session 回放</td>
          <td>原始 7d</td>
      </tr>
      <tr>
          <td>Debug</td>
          <td>最近操作</td>
          <td>{action}.completed</td>
          <td>手動埋點</td>
          <td>開發期起</td>
          <td>breadcrumb trail</td>
          <td>原始 7d</td>
      </tr>
      <tr>
          <td>商業</td>
          <td>漏斗步驟</td>
          <td>funnel.{name}.step_N</td>
          <td>手動埋點</td>
          <td>上線後</td>
          <td>funnel JOIN</td>
          <td>小時聚合 90d</td>
      </tr>
      <tr>
          <td>商業</td>
          <td>功能使用</td>
          <td>feature.{name}.used</td>
          <td>手動埋點</td>
          <td>上線後</td>
          <td>COUNT GROUP BY</td>
          <td>天聚合 365d</td>
      </tr>
      <tr>
          <td>商業</td>
          <td>Session</td>
          <td>session.start/end</td>
          <td>auto-intercept</td>
          <td>上線後</td>
          <td>cohort 留存</td>
          <td>天聚合 365d</td>
      </tr>
      <tr>
          <td>商業</td>
          <td>轉換</td>
          <td>conversion.{type}</td>
          <td>手動埋點</td>
          <td>上線後</td>
          <td>funnel 最後一步</td>
          <td>原始 90d</td>
      </tr>
      <tr>
          <td>資安</td>
          <td>認證失敗</td>
          <td>auth.{method}.failed</td>
          <td>auto-intercept</td>
          <td>開發期起</td>
          <td>閾值告警</td>
          <td>原始 30d</td>
      </tr>
      <tr>
          <td>資安</td>
          <td>權限拒絕</td>
          <td>authz.denied</td>
          <td>auto-intercept</td>
          <td>開發期起</td>
          <td>pattern 偵測</td>
          <td>原始 30d</td>
      </tr>
      <tr>
          <td>資安</td>
          <td>敏感存取</td>
          <td>sensitive.*</td>
          <td>手動埋點</td>
          <td>開發期起</td>
          <td>audit trail</td>
          <td>原始 365d</td>
      </tr>
      <tr>
          <td>資安</td>
          <td>設定變更</td>
          <td>admin.setting.changed</td>
          <td>手動埋點</td>
          <td>開發期起</td>
          <td>audit trail</td>
          <td>原始 365d</td>
      </tr>
      <tr>
          <td>效能</td>
          <td>操作延遲</td>
          <td>{operation}.duration</td>
          <td>手動埋點</td>
          <td>測試期起</td>
          <td>P95 趨勢</td>
          <td>小時聚合 90d</td>
      </tr>
      <tr>
          <td>效能</td>
          <td>渲染效能</td>
          <td>render.frame_drop</td>
          <td>auto-intercept</td>
          <td>測試期起</td>
          <td>趨勢圖</td>
          <td>小時聚合 90d</td>
      </tr>
      <tr>
          <td>效能</td>
          <td>資源用量</td>
          <td>resource.memory</td>
          <td>定期取樣</td>
          <td>測試期起</td>
          <td>趨勢圖</td>
          <td>小時聚合 90d</td>
      </tr>
      <tr>
          <td>效能</td>
          <td>外部依賴</td>
          <td>dependency.{name}.latency</td>
          <td>手動埋點</td>
          <td>測試期起</td>
          <td>GROUP BY 依賴</td>
          <td>小時聚合 90d</td>
      </tr>
      <tr>
          <td>效能</td>
          <td>Web Vitals</td>
          <td>web.vitals</td>
          <td>auto-intercept</td>
          <td>測試期起</td>
          <td>趨勢圖</td>
          <td>小時聚合 90d</td>
      </tr>
      <tr>
          <td>A/B</td>
          <td>實驗分組</td>
          <td>experiment.{name}.assigned</td>
          <td>手動埋點</td>
          <td>實驗期間</td>
          <td>variant GROUP BY</td>
          <td>實驗結束後清</td>
      </tr>
      <tr>
          <td>A/B</td>
          <td>實驗轉換</td>
          <td>experiment.{name}.converted</td>
          <td>手動埋點</td>
          <td>實驗期間</td>
          <td>轉換率計算</td>
          <td>實驗結束後清</td>
      </tr>
      <tr>
          <td>DevOps</td>
          <td>Collector 存活</td>
          <td>collector.health.check</td>
          <td>Collector 內部</td>
          <td>開發期起</td>
          <td>狀態卡</td>
          <td>原始 7d</td>
      </tr>
      <tr>
          <td>DevOps</td>
          <td>事件吞吐量</td>
          <td>collector.ingestion.count</td>
          <td>Collector 內部</td>
          <td>開發期起</td>
          <td>吞吐曲線</td>
          <td>小時聚合 90d</td>
      </tr>
      <tr>
          <td>DevOps</td>
          <td>儲存用量</td>
          <td>collector.storage.disk_usage</td>
          <td>Collector 內部</td>
          <td>開發期起</td>
          <td>儲存圖</td>
          <td>小時聚合 90d</td>
      </tr>
      <tr>
          <td>DevOps</td>
          <td>SDK 心跳</td>
          <td>sdk.heartbeat</td>
          <td>SDK 端</td>
          <td>開發期起</td>
          <td>連線列表</td>
          <td>原始 7d</td>
      </tr>
      <tr>
          <td>DevOps</td>
          <td>部署事件</td>
          <td>deployment.completed</td>
          <td>CI/CD hook</td>
          <td>開發期起</td>
          <td>部署狀態</td>
          <td>原始 30d</td>
      </tr>
      <tr>
          <td>DevOps</td>
          <td>規則命中</td>
          <td>rule.matched</td>
          <td>Collector 內部</td>
          <td>開發期起</td>
          <td>alert 歷史</td>
          <td>原始 30d</td>
      </tr>
      <tr>
          <td>中台</td>
          <td>使用者首次出現</td>
          <td>user.first_seen</td>
          <td>Collector 計算</td>
          <td>上線後</td>
          <td>cohort 分群</td>
          <td>天聚合 365d</td>
      </tr>
      <tr>
          <td>中台</td>
          <td>通路歸因</td>
          <td>attribution.install_source</td>
          <td>SDK 首次啟動</td>
          <td>上線後</td>
          <td>歸因報表</td>
          <td>原始 90d</td>
      </tr>
      <tr>
          <td>中台</td>
          <td>即時在線</td>
          <td>session.active.count</td>
          <td>Collector 計算</td>
          <td>上線後</td>
          <td>即時大屏</td>
          <td>小時聚合 90d</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>四類事件的基礎定義 → <a href="/blog/monitoring/01-mental-model/four-event-types/" data-link-title="四類事件的完整定義" data-link-desc="Event / Error / Metric / Lifecycle 四類事件各自的語意、觸發時機和典型用途 — 分類是監控體系的統一語言">四類事件的完整定義</a></li>
<li>事件枚舉的方法論 → <a href="/blog/monitoring/01-mental-model/event-enumeration-method/" data-link-title="事件枚舉與補齊檢查" data-link-desc="從操作盤點系統性地推導出完整的事件清單 — 四類補齊檢查確保沒有遺漏、粒度判準確保每個事件只記一個事實">事件枚舉與補齊檢查</a></li>
<li>前端感測器的具體設計 → <a href="/blog/monitoring/03-sdk-design/frontend-sensor-design/" data-link-title="前端感測器設計" data-link-desc="什麼行為值得埋感測器、每類感測器的實作方式、取樣策略和效能影響 — 和 auto-intercept 的被動攔截互補">前端感測器設計</a></li>
<li>感測器的生命週期控制 → <a href="/blog/monitoring/03-sdk-design/sensor-lifecycle-management/" data-link-title="感測器生命週期管理" data-link-desc="產品生命週期的五個階段各啟用什麼感測器 — feature flag 整合、取樣率動態調整、感測器開關的可觀察性">感測器生命週期管理</a></li>
<li>查詢消費模式的完整展開 → <a href="/blog/monitoring/04-collector/query-consumption-patterns/" data-link-title="查詢消費模式" data-link-desc="Debug / Alerting / 產品決策 / 安全審計 / 效能監控 — 五種查詢場景各需要什麼事件、什麼欄位、什麼查詢模式">查詢消費模式</a></li>
</ul>
]]></content:encoded></item></channel></rss>