<?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>Jsonl on Tarragon</title><link>https://tarrragon.github.io/blog/tags/jsonl/</link><description>Recent content in Jsonl 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/tags/jsonl/index.xml" rel="self" type="application/rss+xml"/><item><title>JSONL 匯出與備份格式</title><link>https://tarrragon.github.io/blog/monitoring/04-collector/jsonl-storage/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/04-collector/jsonl-storage/</guid><description>&lt;p>Collector 的 day-one 主要儲存是 SQLite（見 &lt;a href="https://tarrragon.github.io/blog/monitoring/04-collector/scaling-evolution/" data-link-title="規模演進" data-link-desc="可插拔 Storage Backend 架構 — SQLite 預設、PostgreSQL 觸發切換、時間序列 DB 長期演進">規模演進&lt;/a>）。JSONL（JSON Lines）保留作為匯出和備份格式 — 人類可讀、grep 友好、SQLite 資料庫損壞時可以從 JSONL 重建。Collector 提供 &lt;code>monitor export --format=jsonl&lt;/code> 指令匯出事件，也可以設定同步寫入 JSONL 作為即時備份。&lt;/p>
&lt;p>JSONL 的格式是每行一個 JSON 物件。作為匯出格式，核心優勢是工具鏈成熟 — &lt;code>grep&lt;/code> 過濾、&lt;code>jq&lt;/code> 結構化查詢、&lt;code>tail -f&lt;/code> 即時監控，不需要 database client。&lt;/p>
&lt;h2 id="一天一檔">一天一檔&lt;/h2>
&lt;p>事件按日期分檔：&lt;code>events-2026-06-19.jsonl&lt;/code>、&lt;code>events-2026-06-20.jsonl&lt;/code>。每天零點（或 UTC 日期變更時）切換到新檔案。&lt;/p>
&lt;p>一天一檔的好處：&lt;/p>
&lt;p>&lt;strong>時間範圍查詢直接對應到檔案&lt;/strong>。查「昨天的 error」只需要讀一個檔案，不需要掃描整個資料集。&lt;/p>
&lt;p>&lt;strong>保留策略按檔案操作&lt;/strong>。保留 30 天的資料 = 刪除 30 天前的檔案。不需要 database 的 TTL 機制或 partition pruning。&lt;/p>
&lt;p>&lt;strong>備份和搬移按檔案操作&lt;/strong>。rsync 一個目錄就完成備份；搬移特定日期的資料 = 搬移對應檔案。&lt;/p>
&lt;p>一天一檔的風險是單日資料量過大時，單一檔案的 grep 查詢會變慢。自用工具場景下，單日事件量通常在數千到數萬筆，檔案大小在 MB 級，grep 查詢在秒級完成。當單日事件量超過百萬筆時，需要考慮演進到更適合的儲存方案（見 &lt;a href="https://tarrragon.github.io/blog/monitoring/04-collector/scaling-evolution/" data-link-title="規模演進" data-link-desc="可插拔 Storage Backend 架構 — SQLite 預設、PostgreSQL 觸發切換、時間序列 DB 長期演進">規模演進&lt;/a>）。&lt;/p>
&lt;h2 id="append-only-寫入">Append-only 寫入&lt;/h2>
&lt;p>JSONL 的寫入模式是 append-only — 新事件追加到檔案尾端，已寫入的事件不修改。&lt;/p>
&lt;p>Append-only 的操作特性：&lt;/p>
&lt;p>&lt;strong>寫入不需要鎖&lt;/strong>。&lt;code>os.OpenFile&lt;/code> 用 &lt;code>O_APPEND&lt;/code> flag 開啟，OS 保證每次 write 是 atomic 的（在 write size 不超過 &lt;code>PIPE_BUF&lt;/code> 的前提下，Linux 上是 4096 bytes）。單一事件的 JSON 通常在這個限制內。&lt;/p>
&lt;p>&lt;strong>不會損壞既有資料&lt;/strong>。寫入失敗（磁碟滿、程序崩潰）最多造成最後一行不完整，不影響前面的行。恢復時刪除最後一行的不完整片段即可。&lt;/p>
&lt;p>&lt;strong>支援 tail -f 即時監控&lt;/strong>。&lt;code>tail -f events-2026-06-19.jsonl | jq .&lt;/code> 即時顯示新寫入的事件，不需要額外的 streaming 機制。&lt;/p>
&lt;h2 id="gzip-壓縮">Gzip 壓縮&lt;/h2>
&lt;p>歷史檔案（非當天的）用 gzip 壓縮。JSON 文字的壓縮率通常在 80-90%（10MB 壓縮到 1-2MB）。&lt;/p>
&lt;p>壓縮策略：&lt;/p>
&lt;p>&lt;strong>當天的檔案不壓縮&lt;/strong>。保持 append-only 和 tail -f 的能力。&lt;/p>
&lt;p>&lt;strong>日期切換時壓縮前一天的檔案&lt;/strong>。用 cron job 或 collector 啟動時檢查，把 &lt;code>events-2026-06-18.jsonl&lt;/code> 壓縮為 &lt;code>events-2026-06-18.jsonl.gz&lt;/code>。&lt;/p>
&lt;p>&lt;strong>查詢壓縮檔用 zgrep / zcat&lt;/strong>。&lt;code>zgrep &amp;quot;error&amp;quot; events-2026-06-18.jsonl.gz&lt;/code> 不需要先解壓。&lt;/p>
&lt;h2 id="jsonl-備份的保留">JSONL 備份的保留&lt;/h2>
&lt;p>JSONL 備份檔的保留策略和 SQLite 主要儲存的分層保留獨立 — JSONL 是最後的重建來源，保留期限可以比 SQLite 中的原始事件更長。&lt;/p>
&lt;p>典型配置：JSONL 備份保留 30 天（即使 SQLite 中的原始事件只保留 7 天），提供 SQLite 損壞時的 30 天重建窗口。超過 30 天的 JSONL 壓縮檔用 cron job 清理：&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">find /var/lib/collector/events/ -name &lt;span class="s2">&amp;#34;events-*.jsonl.gz&amp;#34;&lt;/span> -mtime +30 -delete&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>主要儲存的查詢驅動分層保留策略見 &lt;a href="https://tarrragon.github.io/blog/monitoring/04-collector/scaling-evolution/" data-link-title="規模演進" data-link-desc="可插拔 Storage Backend 架構 — SQLite 預設、PostgreSQL 觸發切換、時間序列 DB 長期演進">規模演進&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>Collector 的 day-one 主要儲存是 SQLite（見 <a href="/blog/monitoring/04-collector/scaling-evolution/" data-link-title="規模演進" data-link-desc="可插拔 Storage Backend 架構 — SQLite 預設、PostgreSQL 觸發切換、時間序列 DB 長期演進">規模演進</a>）。JSONL（JSON Lines）保留作為匯出和備份格式 — 人類可讀、grep 友好、SQLite 資料庫損壞時可以從 JSONL 重建。Collector 提供 <code>monitor export --format=jsonl</code> 指令匯出事件，也可以設定同步寫入 JSONL 作為即時備份。</p>
<p>JSONL 的格式是每行一個 JSON 物件。作為匯出格式，核心優勢是工具鏈成熟 — <code>grep</code> 過濾、<code>jq</code> 結構化查詢、<code>tail -f</code> 即時監控，不需要 database client。</p>
<h2 id="一天一檔">一天一檔</h2>
<p>事件按日期分檔：<code>events-2026-06-19.jsonl</code>、<code>events-2026-06-20.jsonl</code>。每天零點（或 UTC 日期變更時）切換到新檔案。</p>
<p>一天一檔的好處：</p>
<p><strong>時間範圍查詢直接對應到檔案</strong>。查「昨天的 error」只需要讀一個檔案，不需要掃描整個資料集。</p>
<p><strong>保留策略按檔案操作</strong>。保留 30 天的資料 = 刪除 30 天前的檔案。不需要 database 的 TTL 機制或 partition pruning。</p>
<p><strong>備份和搬移按檔案操作</strong>。rsync 一個目錄就完成備份；搬移特定日期的資料 = 搬移對應檔案。</p>
<p>一天一檔的風險是單日資料量過大時，單一檔案的 grep 查詢會變慢。自用工具場景下，單日事件量通常在數千到數萬筆，檔案大小在 MB 級，grep 查詢在秒級完成。當單日事件量超過百萬筆時，需要考慮演進到更適合的儲存方案（見 <a href="/blog/monitoring/04-collector/scaling-evolution/" data-link-title="規模演進" data-link-desc="可插拔 Storage Backend 架構 — SQLite 預設、PostgreSQL 觸發切換、時間序列 DB 長期演進">規模演進</a>）。</p>
<h2 id="append-only-寫入">Append-only 寫入</h2>
<p>JSONL 的寫入模式是 append-only — 新事件追加到檔案尾端，已寫入的事件不修改。</p>
<p>Append-only 的操作特性：</p>
<p><strong>寫入不需要鎖</strong>。<code>os.OpenFile</code> 用 <code>O_APPEND</code> flag 開啟，OS 保證每次 write 是 atomic 的（在 write size 不超過 <code>PIPE_BUF</code> 的前提下，Linux 上是 4096 bytes）。單一事件的 JSON 通常在這個限制內。</p>
<p><strong>不會損壞既有資料</strong>。寫入失敗（磁碟滿、程序崩潰）最多造成最後一行不完整，不影響前面的行。恢復時刪除最後一行的不完整片段即可。</p>
<p><strong>支援 tail -f 即時監控</strong>。<code>tail -f events-2026-06-19.jsonl | jq .</code> 即時顯示新寫入的事件，不需要額外的 streaming 機制。</p>
<h2 id="gzip-壓縮">Gzip 壓縮</h2>
<p>歷史檔案（非當天的）用 gzip 壓縮。JSON 文字的壓縮率通常在 80-90%（10MB 壓縮到 1-2MB）。</p>
<p>壓縮策略：</p>
<p><strong>當天的檔案不壓縮</strong>。保持 append-only 和 tail -f 的能力。</p>
<p><strong>日期切換時壓縮前一天的檔案</strong>。用 cron job 或 collector 啟動時檢查，把 <code>events-2026-06-18.jsonl</code> 壓縮為 <code>events-2026-06-18.jsonl.gz</code>。</p>
<p><strong>查詢壓縮檔用 zgrep / zcat</strong>。<code>zgrep &quot;error&quot; events-2026-06-18.jsonl.gz</code> 不需要先解壓。</p>
<h2 id="jsonl-備份的保留">JSONL 備份的保留</h2>
<p>JSONL 備份檔的保留策略和 SQLite 主要儲存的分層保留獨立 — JSONL 是最後的重建來源，保留期限可以比 SQLite 中的原始事件更長。</p>
<p>典型配置：JSONL 備份保留 30 天（即使 SQLite 中的原始事件只保留 7 天），提供 SQLite 損壞時的 30 天重建窗口。超過 30 天的 JSONL 壓縮檔用 cron job 清理：</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">find /var/lib/collector/events/ -name <span class="s2">&#34;events-*.jsonl.gz&#34;</span> -mtime +30 -delete</span></span></code></pre></div><p>主要儲存的查詢驅動分層保留策略見 <a href="/blog/monitoring/04-collector/scaling-evolution/" data-link-title="規模演進" data-link-desc="可插拔 Storage Backend 架構 — SQLite 預設、PostgreSQL 觸發切換、時間序列 DB 長期演進">規模演進</a>。</p>
<h2 id="匯出的實作注意">匯出的實作注意</h2>
<p>匯出使用 streaming — 從 storage 逐筆讀取、逐行寫出，記憶體使用和事件總量無關。300 萬筆事件（約 900MB JSONL）不需要整批載入記憶體。</p>
<p>匯出的 JSONL 檔案包含事件明文（已 redaction 的欄位除外）。匯出後的檔案不受 collector 的存取控制保護，注意存放位置和存取權限。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Collector 的完整架構 → <a href="/blog/monitoring/04-collector/architecture/" data-link-title="Collector 架構" data-link-desc="HTTP endpoint → JSON Schema 驗證 → 儲存 → 查詢 → rule engine 的五段式處理鏈路">Collector 架構</a></li>
<li>查詢設計 → <a href="/blog/monitoring/04-collector/query-api/" data-link-title="查詢 API 設計" data-link-desc="CLI grep 友好的 JSONL 結構 &#43; HTTP 查詢 endpoint — 兩種查詢介面各自的適用場景和設計要點">查詢 API 設計</a></li>
<li>儲存撐不住時的演進 → <a href="/blog/monitoring/04-collector/scaling-evolution/" data-link-title="規模演進" data-link-desc="可插拔 Storage Backend 架構 — SQLite 預設、PostgreSQL 觸發切換、時間序列 DB 長期演進">規模演進</a></li>
</ul>
]]></content:encoded></item></channel></rss>