ข้าม​ไป​ยัง​เนื้อหา

สำรวจ & visualize ใน HyperDX

ห้า​บท​ที่​ผ่าน​มา​คุณ ปล่อย​สัญญาณ ออก​มา​ครบ — auto-trace end-to-end (บท2), custom span ที่​ข้าม RabbitMQ เป็น trace เดียว (บท3), RED metric ที่​ไม่​ระเบิด cardinality (บท4), และ log ที่ correlate เข้า trace อัตโนมัติ (บท5) ตอน​นี้ store ของ​คุณ​เต็ม​ไป​ด้วย telemetry ที่​สัมพันธ์​กัน แต่​ยัง ถาม​มัน​ไม่​เป็น บท​นี้​คือ​บท​ที่​คุณ​เปิด HyperDXHyperDXUI ของ ClickStack สำหรับ search (Lucene)/SQL/dashboard/alert/trace waterfall เหนือ store เดียวกัน — บท​เดียว​ที่​ผูก​กับ sink โดย​ตั้งใจArchitecture แล้ว​เริ่ม ตั้ง​คำถาม​ที่​คุณ​ไม่​ได้​เตรียม​ไว้​ก่อน

⚠️ บท​นี้​ผูก​กับ HyperDX โดย​ตั้งใจ — ที่​เหลือ​ของ​คอร์ส portable

ตลอด​คอร์ส​เรา​ย้ำ​เส้น​ความ​ซื่อสัตย์​ข้อ (A): “สิ่ง​ที่​คุณ instrument คือ OpenTelemetry — มาตรฐาน​เปิด​ที่​เป็นกลาง​ต่อ vendor. ClickStack เป็น​แค่ ‘ปลายทาง’ (sink) ที่​เรา​เลือก​ใช้​ให้​จับ​ต้อง​ได้ ไม่ใช่​ตัว​คอร์ส.” — code instrument ใน C# ไม่​ผูก​กับ​ปลายทาง​เลย สลับ sink เป็น Jaeger/Grafana/Datadog ได้​โดย​ไม่​แก้ code แต่​บท​นี้​คือ​ข้อ​ยกเว้น​ที่​ตั้งใจ Lucene syntax, ชื่อ​ตาราง SQL, ชนิด tile ของ dashboard — พวก​นี้​เป็น​ของ HyperDX เฉพาะ​ตัว ถ้า​วัน​หนึ่ง​คุณ​ย้าย​ไป Grafana/Tempo ทักษะ​การ​ตั้ง​คำถาม (pivot จาก metric → trace → log) จะ​ติดตัว​ไป แต่ ไวยากรณ์ ต้อง​เรียน​ใหม่ เรา​พูดตรงๆ ตรง​นี้: บท​นี้​ไม่ portable และ​นั่น​คือ​ความ​ตั้งใจ

📦 code ตัวอย่าง

บท​นี้ query telemetry ที่​บท2–บท5 ปล่อย​จาก platform Order (Ordering/Kitchen/Delivery/Payment) — repo kaen-food-ordering (กำลัง​จัด​ทำ) ใช้ order-placement load script จาก​บท2 รัน​ไว้​สัก​พัก​ก่อน เพื่อ​ให้ store มี trace/log/metric ให้​ค้น​จริง ทุก query ใน​บท​นี้​อ้าง service.name และ attribute ที่​คุณ​ตั้ง​เอง​ใน​บท​ก่อน (order.id, payment.outcome) — ไม่มี​อะไร​ใหม่​ต้อง instrument เพิ่ม บท​นี้​ล้วนๆ คือ อ่าน สิ่ง​ที่​ปล่อย​ไว้​แล้ว

ย้อน​เส้น​ความ​ซื่อสัตย์​ข้อ (B): monitoring ตอบ​คำถาม​ที่​คุณ รู้​ล่วงหน้า​ว่า​จะ​ถาม — dashboard ที่​สร้าง​ไว้​ก่อน ส่วน observability คือ​ความ​สามารถ ตั้ง​คำถาม​ใหม่ กับ​ข้อมูล​ที่​ระบบ​ปล่อย​ไว้​แล้ว บท​นี้​คือ​ที่​ที่​ความ​ต่าง​นั้น​จับ​ต้อง​ได้​จริง

สถานการณ์: มี​คน​บ่น​ว่า “ออเดอร์ #4821 ใช้​เวลา 40 วิ” คุณ ไม่มี dashboard ชื่อ “ทำไม​ออเดอร์​นี้​ช้า” ไว้​ก่อน — เพราะ​ไม่มี​ใคร​คาด​เหตุ​นี้​ได้ แต่​คุณ​มี​ทุก span/log/metric ที่ platform ปล่อย​ไว้​แล้ว​ใน 30 นาที​ที่​ผ่าน​มา งาน​ของ​บท​นี้​คือ ขุด คำ​ตอบ​ออก​มา​จาก​ข้อมูล​ดิบ​นั้น ผ่าน​สี่​เครื่องมือ​ของ HyperDX: Lucene search, ClickHouse SQL, trace waterfall, และ dashboard

หัวใจ​ของ HyperDX คือ​ทุก​สัญญาณ (traces/logs/metrics) ถูก​เก็บ​เป็น​แถว columnar กว้างๆ ใน ClickHouse — store เดียว ไม่ใช่​สาม​คลัง​แยก​กัน (นี่​คือ Wide EventWide Eventเหตุการณ์ structured ที่​มี attribute จำนวน​มาก (คือส​แปน​นั่นเอง) เป็น​หน่วย​ข้อมูล​หลัก​ของ Obs 2.0Architecture ที่​เรา​คุย​กัน​มา​ตั้งแต่​บท1/บท5: structured event ที่​มี attribute กว้างๆ ก็​คือ span นั่นเอง) เหนือ store เดียวกัน​นั้น HyperDX ให้​คุณ​ถาม​ได้​สอง​แบบ (S21):

  • Lucene-style search — พิมพ์​เร็ว สำหรับ triage: HyperDX transpile มัน​เป็น ClickHouse SQL WHERE ให้​เบื้องหลัง (S27)
  • ClickHouse SQL เต็ม​รูป — สำหรับ aggregation/join ที่ Lucene ทำ​ไม่​ได้ (p95, group-by, correlate ข้าม​ตาราง) (S21)

คิด​ง่ายๆ: Lucene = ตะแกรง​ร่อน​เร็ว, SQL = มีด​ผ่าตัด เริ่ม​ด้วย Lucene เพื่อ​แคบ​ขอบเขต แล้ว​สลับ​ไป SQL เมื่อ​คำถาม​ซับซ้อน​เกิน​ไวยากรณ์​ตะแกรง

Lucene bar เหมาะ​กับ​การ แคบ​ผลลัพธ์ ให้​เร็ว​ที่สุด ต่อ​ไป​นี้​คือ​ไวยากรณ์​ที่​คุณ​จะ​ใช้​จริง​กับ telemetry ของ Order platform (S27):

# field match — log level Error เฉพาะ ordering-service
ServiceName:ordering-service SeverityText:Error
# boolean + negation — event ของ kitchen-service ที่ไม่ใช่ระดับ Info
ServiceName:kitchen-service AND NOT SeverityText:Info
# quoted phrase — ข้อความ log ตรงตัว
Body:"payment declined"
# comparison — span ที่ช้ากว่า 1000 (หน่วยตาม field; ดู ⚠️ ด้านล่าง)
Duration:>1000
# existence — เฉพาะ record ที่ *มี* attribute order.id (คือ span/log ของ Order flow)
OrderId:*
# wildcard บางส่วน — จับทุกอย่างที่มีคำว่า Timeout
*Timeout*

ตัวอย่าง​จริง​ตอน triage: อยาก​ดู​ว่า​มี log “payment declined” ของ payment-service ใน​ชั่วโมง​นี้​ไหม พิมพ์ ServiceName:payment-service Body:"payment declined" แล้ว​ปรับ time range ที่​มุม​ขวา​บน — ได้​ผล​ใน 2 วินาที ไม่​ต้อง​เขียน SQL

ขอบเขต​ของ Lucene: อะไร​ที่​มัน​ทำ 'ไม่​ได้' → สลับ​ไป SQL

Lucene bar ของ HyperDX ไม่​รองรับ regex, fuzzy match, และ wildcard กลาง​คำ (in-term)*Timeout* (ขึ้น​ต้น/ลงท้าย​ด้วย *) ใช้ได้ แต่ pattern กลาง​คำ​แบบ Time*out หรือ regex /ord.*/ ทำ​ไม่​ได้ พอ​เจอ​เพดาน​นี้​ให้​สลับ​ไป ClickHouse SQL ซึ่ง​มี​ทั้ง match() (regex), LIKE, และ aggregation เต็ม​รูป — Lucene คือ​ความเร็ว SQL คือ​พลัง

เมื่อ​คำถาม​ต้องการ สรุป​รวม (p95, count group-by, join ข้าม trace) Lucene หมด​แรง — เปิด SQL editor แล้ว query ตรงๆ เหนือ schema ที่ ClickStack collector เขียน​ไว้ ตาราง​หลัก​ที่​คุณ​จะ​ใช้ (S26):

  • otel_traces — ทุก span
  • otel_logs — ทุก log record (มี TraceId/SpanId ที่ correlate มา​จาก​บท5)
  • otel_metrics_* — metric ห้า​ตาราง แยก​ตาม​ชนิด instrument (sum/gauge/histogram/exponential-histogram/summary)
⚠️ รายละเอียด schema ที่​ต้อง​เช็กกับ deployment ของ​คุณ​เอง

3 detail ต่อ​ไป​นี้ ต่าง​กัน​ได้ ระหว่าง version ClickStack — เรา​สอน​ค่าที่​พบ​บ่อย แต่​ให้​เปิด​หน้า schema ใน deployment ของ​คุณ​ยืนยัน​ก่อน copy-paste (S26):

  • Duration เป็น UInt64 หน่วย nanosecond → ต้อง​หาร /1e6 เพื่อ​ได้ millisecond (บาง query ตัวอย่าง​ข้าง​ล่าง​พึ่ง​ค่า​นี้ ถ้า deployment ของ​คุณ​เก็บ​เป็น​หน่วย​อื่น ตัวเลข​จะ​เพี้ยน)
  • database เริ่มต้น​ชื่อ default — แต่​บาง deployment ใช้ otel ถ้า query ฟ้อง table not found ลอง​เติม prefix otel.otel_traces
  • SpanAttributes/ResourceAttributes/LogAttributes เป็น​ชนิด Map → ใน SQL เข้าถึง​ด้วย bracket SpanAttributes['http.route'] แต่ ใน Lucene bar การ​อ้าง key ของ Map อาจ​ใช้​รูปแบบ​ต่าง​กัน (แล้ว​แต่ว่า field ถูก promote เป็น top-level หรือ​ไม่) — ยืนยัน​กับ search docs ของ version คุณ

Query 1 — p95 latency ต่อ service (คำถาม “service ไหน​ช้า​ที่สุด​ตอน​นี้”):

-- p95 latency per service (Duration หน่วยเป็น nanosecond → /1e6 = ms)
SELECT ServiceName, quantile(0.95)(Duration)/1e6 AS p95_ms
FROM otel_traces
WHERE Timestamp >= now() - INTERVAL 1 HOUR AND SpanAttributes['http.route'] = '/orders'
GROUP BY ServiceName ORDER BY p95_ms DESC;

นี่​คือ query ที่ Lucene ทำ​ไม่​ได้ — quantile(0.95) เป็น aggregation ที่​ต้อง​มอง​ทุก​แถว​ใน 1 ชั่วโมง แล้ว​สรุป​เป็น​ตัวเลข​เดียว​ต่อ service สังเกต /1e6 ที่​แปลง nanosecond → millisecond และ SpanAttributes['http.route'] ที่ index เข้า Map column

Query 2 — ทุก span ของ trace เดียว (สิ่ง​ที่​ป้อน​ให้ waterfall):

-- all spans of one trace (feeds the waterfall)
SELECT Timestamp, ServiceName, SpanName, SpanKind, Duration, StatusCode, ParentSpanId, SpanId
FROM otel_traces WHERE TraceId = '0af7651916cd43dd8448eb211c80319c' ORDER BY Timestamp;

ParentSpanId/SpanId คือ2 column ที่​ประกอบ​ร่าง​เป็นต้นไม้ waterfall — แต่ละ​แถว​รู้​ว่า parent ของ​ตัวเอง​คือ​ใคร เรียง​ตาม Timestamp แล้ว​คุณ​จะ​เห็น​ลำดับ Ordering → (RabbitMQ) → Kitchen + Delivery ตาม​ที่​บท3 สร้าง​ไว้

คุณ​ไม่​จำเป็น​ต้อง​เขียน Query 2 ด้วย​มือ​ทุก​ครั้ง — ใน HyperDX คลิก​ที่ trace หนึ่ง​ใบ มัน​จะ render span waterfall ให้​อัตโนมัติ: แต่ละ span เป็น​แท่ง​แนว​นอน ความ​ยาว = duration เยื้อง​เข้า​ตาม​ความ​ลึก parent-child เห็น​ทันที​ว่า​เวลา 40 วิ ไป​หมด​กับ span ไหน

trace ของ Order flow มี span เยอะ (Ordering server span, EF Core DB span, Wolverine send/receive, Kitchen handler, Delivery handler…) ตอน​สแกน​หา trace ที่​ผิด​ปกติ ให้ filter ผลลัพธ์​เหลือ​เฉพาะ root span — หนึ่ง​แถว​ต่อ1 request ทำให้​ไล่​สายตา​หา request ที่​ช้า​ได้​ก่อน แล้ว​ค่อย​เจาะ​เข้า waterfall ของ​ใบ​นั้น (S49)

flowchart LR
  Q["คำถามที่ไม่ได้เตรียมไว้ก่อน<br/>'ทำไมออเดอร์ #4821 ช้า 40 วิ?'"]
  subgraph HDX["HyperDX เหนือ store เดียว (ClickHouse)"]
    direction TB
    L["Lucene search<br/>ตะแกรงร่อนเร็ว<br/>transpile → SQL WHERE"]
    S["ClickHouse SQL<br/>มีดผ่าตัด<br/>p95 / group-by / join"]
    W["Trace waterfall<br/>คลิก trace → เห็น span tree<br/>filter root-span-only"]
    D["Dashboard<br/>tiles + global filter + time range"]
  end
  A["คำตอบ + pivot ต่อ<br/>ไป all orders ที่ share handler ช้า"]
  Q --> L
  Q --> S
  L --> W
  S --> W
  W --> A
  S --> D
  D --> A

  classDef q fill:#0e7490,stroke:#155e75,color:#f0fdff;
  classDef tool fill:#1e293b,stroke:#0f172a,color:#e2e8f0;
  class Q,A q;
  class L,S,W,D tool;

คำ​บรรยาย​ภาพ: คำถาม​ที่​ไม่​ได้​เตรียม​ไว้​ล่วงหน้า​เข้า​ทาง​ซ้าย เลือก​ได้​สอง​พื้น​ผิว query เหนือ store เดียวกัน — Lucene สำหรับ​ร่อน​เร็ว, SQL สำหรับ aggregation ที่ Lucene ทำ​ไม่​ได้ ทั้ง​คู่​ป้อน​ต่อ​เข้า trace waterfall (คลิก trace แล้ว​เห็น span tree, filter เหลือ root span เพื่อ​สแกน) และ SQL ยัง​ต่อ​เข้า dashboard ได้ ปลายทาง​คือ​คำ​ตอบ​และ​การ pivot ต่อ​ไป​ยัง​ทุกออเดอร์​ที่ share ปัญหา​เดียวกัน

พอ​คุณ เจอ pattern ที่​อยาก​จับ​ตา​ซ้ำๆ ให้​ปั้น​มัน​เป็น dashboard — HyperDX ให้ tile หลาย​ชนิด: Line/Bar (แนวโน้ม​ตาม​เวลา), Number (ตัวเลข​เดียว เช่น p95 ปัจจุบัน), Table (top-N), Heatmap (การกระจาย​ของ latency), Pie (สัดส่วน error ต่อ context) ทุก tile บน​หน้า​เดียวกัน share global filter และ time range เดียวกัน — เลื่อน time picker ที​เดียว ทุก tile ขยับ​ตาม (S29)

tile ที่​เขียน​ด้วย SQL ใช้ macro ช่วย​ให้ chart interactive โดย​ไม่​ต้อง hard-code (S30):

-- SQL chart tile: error count ต่อ context, เคารพ global filter + time range ของ dashboard
SELECT ServiceName, count() AS errors
FROM $__sourceTable
WHERE StatusCode = 'Error' AND $__filters
GROUP BY ServiceName ORDER BY errors DESC;

$__sourceTable ถูก​แทน​ด้วย​ตาราง​ที่ tile ชี้​อยู่ และ $__filters ถูก​แทน​ด้วย global filter + time range ที่​ผู้​ดู dashboard เลือก ณ ตอน​นั้น (convention เดียว​กับ ClickHouse Grafana plugin เช่น param {startDateMilliseconds:Int64}) — เขียน query ครั้ง​เดียว ใช้ได้​ทุก filter (S30)

⚠️ ชนิด tile กับ​ชื่อ macro อาจ​ต่าง​ตาม version

รายการ​ชนิด tile และ​ชื่อ macro ($__filters/$__sourceTable) ข้าง​บน​อ้าง​จาก​เอกสาร dashboard + block SQL charting ณ ช่วง​ที่​เขียน (S29, S30) HyperDX ยัง​พัฒนา​ต่อ​เนื่อง — เปิด “Add tile” ใน deployment ของ​คุณ​เพื่อ​ดู​รายการ​ชนิด​จริง และ​เช็กชื่อ macro ใน​หน้า SQL chart ก่อน copy-paste เรา​สอน แนวคิด (tile + shared filter + macro) ให้​แน่น ส่วน​ชื่อ​เป๊ะๆ ให้​ยืนยัน​กับ UI

Payoff: pivot จาก ‘1 trace ช้า’ ไป ‘ทุกออเดอร์​ที่​เจอ​ปัญหา​เดียวกัน’

หัวข้อ​ที่​มีชื่อ​ว่า “Payoff: pivot จาก ‘1 trace ช้า’ ไป ‘ทุกออเดอร์​ที่​เจอ​ปัญหา​เดียวกัน’”

นี่​คือ​ช่วง​ที่​ทุก​อย่าง​มา​บรรจบ และ​เป็น​สิ่ง​ที่ Wide Event / Observability 2.0 ซื้อ ให้​คุณจริงๆ:

  1. คุณ​เปิด trace ของออเดอร์ #4821 ใน waterfall เห็น​ว่า​เวลา 40 วิไป​หมด​กับ span เดียว — span ที่ Kitchen context ประมวล​ผล OrderConfirmedIntegrationEvent (ชื่อ span ที่ HyperDX แสดง​จริง​อ่าน​ได้​จาก waterfall — ดู ⚠️ ใต้ query)
  2. คุณ​อยาก​รู้ทันที​ว่า “ออเดอร์​อื่น​เจอ span ช้า​ตัว​เดียวกัน​ไหม กี่​ใบ?” — แต่​คุณ ไม่​เคย​ตั้ง metric ชื่อ “kitchen handler duration by order” ไว้​ก่อน เพราะ​ไม่มี​ใคร​คาด​เหตุ​นี้
  3. ใน​โลก metric-only คุณ​จบ​ตรง​นี้ — ถาม​ไม่​ได้​เพราะ​ไม่​ได้ pre-aggregate ไว้ แต่​เพราะ​ทุก span คือ wide event ที่​เก็บ order.id เป็น attribute (บท4 บอก​ไว้: OrderId เป็น span attribute ไม่ใช่ metric label — traces รับ high cardinality ได้) คุณ query มันสดๆ ได้​เลย:
-- ทุกออเดอร์ที่ share Kitchen handler ตัวช้า ในชั่วโมงที่ผ่านมา — ไม่มี metric ตั้งไว้ล่วงหน้า
SELECT SpanAttributes['order.id'] AS order_id, Duration/1e6 AS ms
FROM otel_traces
WHERE ServiceName = 'kitchen-service'
AND SpanName = 'WhenOrderConfirmed' -- ⚠️ placeholder — Wolverine ตั้งชื่อ span ตามชนิด message ไม่ใช่ชื่อ handler; อ่านชื่อจริงจาก waterfall (ดู callout ใต้ query)
AND Duration/1e6 > 5000
AND Timestamp >= now() - INTERVAL 1 HOUR
ORDER BY ms DESC;
⚠️ `SpanName` ใน​ตัวอย่าง​เป็น placeholder — อ่าน​ชื่อ​จริง​จาก waterfall ก่อน copy-paste

WhenOrderConfirmed ที่​กรอง​ด้าน​บน​คือ ชื่อ handler class ที่​เรา​ใส่​ให้​เห็น​ภาพ — แต่ Wolverine ตั้ง​ชื่อ span ของ​การ​ประมวล​ผล message ตาม ชนิด​ของ message (ตาม OTel messaging semconv) ไม่ใช่​ชื่อ handler และ​ชื่อ​ที่ HyperDX แสดง​จริง​ยัง ต่าง​ได้​ตาม version/config ของ Wolverine ถ้า SpanName ที่​กรอง​ไม่​ตรง​กับ​ที่​ระบบ​คุณ​ปล่อย​จริง query จะ​คืน ศูนย์​แถว​เงียบๆ — คือ​กับดัก silent-empty-result ที่​บท​นี้​เตือน​ไว้​พอดี ก่อน copy-paste ให้​เปิด waterfall ของ trace ช้า​จริง​สัก​ใบ อ่าน​ค่า SpanName ของ span Kitchen ที่​ช้า แล้ว​เอา​ค่า​นั้น​มา​แทน — หรือ​กรอง​ด้วย​สิ่ง​ที่​เสถียร​กว่า เช่น ServiceName = 'kitchen-service' บวก SpanAttributes['order.id'] != '' และ Duration threshold โดย​ไม่​พึ่ง​ชื่อ span

คุณ​เพิ่ง​ตอบ​คำถาม​ที่ ไม่มี​ใคร​เตรียม dashboard ไว้ — เพราะ high-cardinality order.id ถูก​เก็บ​เป็น attribute บน span (ไม่ใช่ label บน metric ที่​จะ​ทำ cardinality ระเบิด) คุณ​จึง pivot จาก หนึ่ง trace ที่​ผิด​ปกติ ไปหา ทุก ออเดอร์​ที่ share อาการ​เดียวกัน​ได้​ใน​บรรทัด​เดียว นี่​คือ​ความ​ต่าง​ระหว่าง observability (ตั้ง​คำถาม​ใหม่​ได้) กับ monitoring (ตอบ​ได้​แค่​ที่​เดา​ไว้) ที่​จับ​ต้อง​ได้ที่สุด​ใน​ทั้ง​คอร์ส

🔁 เชื่อม​โยง​ข้าม​บท

ความ​สามารถ pivot นี้ ยืม การ​ตัดสิน​ใจ​จาก​บท​ก่อน​มา​ทั้งหมด: บท3 ทำให้ span ของ Kitchen อยู่​ใน trace เดียว​กับ Ordering ข้าม RabbitMQ, บท4 ตัดสิน​ใจ​เก็บ order.id เป็น span attribute (ไม่ใช่ metric label), บท5 ทำให้ log ของ payment click กลับ​เข้า trace ได้ บท​นี้​แค่ เก็บเกี่ยว — ซึ่ง​ตอกย้ำ​เส้น​ข้อ (D): คุณ debug ได้​เฉพาะ​สิ่ง​ที่​คุณ instrument ไว้ ถ้า​บท4 เผลอ​ทำ order.id เป็น metric label คุณ​จะ​ทั้ง​จ่าย​เงิน​แพง​และ ยัง ตอบ​คำถาม​นี้​ไม่​ได้​อยู่ดี

บท​นี้​คือ​ข้อ​ยกเว้น​ที่​ตั้งใจ — บท​เดียว​ของ​คอร์ส​ที่​ผูก​กับ HyperDX เพราะ Lucene syntax/ชื่อ​ตาราง SQL/ชนิด tile เป็น​ของ HyperDX เฉพาะ​ตัว (ที่​เหลือ​ของ​คอร์ส portable) HyperDX ให้​สอง​พื้น​ผิว query เหนือ store เดียว: Lucene (ตะแกรง​ร่อน​เร็ว, transpile → SQL WHERE, ไม่มี regex/fuzzy/wildcard กลาง​คำ) และ ClickHouse SQL (มีด​ผ่าตัด, aggregation เต็ม​รูป) — จำ​ไว้​ว่า Duration เป็น nanosecond ต้อง /1e6 และ SpanAttributes เป็น Map ต้อง index ด้วย ['key'] trace waterfall เปลี่ยน query เป็น​ภาพ (filter root-span-only เพื่อ​สแกน) และ dashboard เก็บ pattern ที่​เจอ​บ่อย​เป็น tile ที่ share filter/time range ผ่าน macro $__filters/$__sourceTable และ payoff ที่​ใหญ่​ที่สุด: เพราะ​ทุก span เป็น wide event ที่​เก็บ order.id เป็น attribute คุณ pivot จาก1 trace ช้า​ไป​หา​ทุกออเดอร์​ที่ share ปัญหา​เดียวกัน​ได้ โดย​ไม่​ต้อง​มี metric ตั้ง​ไว้​ล่วงหน้า

บท​หน้า​เรา​จะ​เลิก​ถาม ตอน​เกิด​เหตุ แล้ว​หัน​มา​ถาม​ว่า “มัน​พัง​จริง​หรือ​ยัง?” — SLI/SLO/error budget และ burn-rate alert ที่​ยิง​เมื่อ​อาการ​กระทบ​ผู้​ใช้​จริง ไม่ใช่ noise ตาม​สาเหตุ


🔗 อ้างอิง​ต้นทาง​ของ​บท​นี้

บท​นี้​อิง​ต้นทาง​ที่​ลง​วัน​ที่​กำกับ อ่าน​ต่อ​ได้​โดยตรง:

  • ClickStack — Overview (เอกสาร​ทางการ, เข้าถึง 2026-07-22) — 3 component (HyperDX + ClickHouse + OTel Collector), สอง​พื้น​ผิว query (Lucene + SQL) เหนือ store เดียว [S21]
  • ClickStack — Schemas (เอกสาร​ทางการ, เข้าถึง 2026-07-22) — ตาราง otel_logs/otel_traces/otel_metrics_*; Duration เป็น UInt64 nanosecond; SpanAttributes/ResourceAttributes เป็น Map [S26]
  • ClickStack — Search (เอกสาร​ทางการ, เข้าถึง 2026-07-22) — Lucene-style syntax ที่ transpile เป็น ClickHouse SQL WHERE; ขอบเขต (ไม่มี regex/fuzzy/in-term wildcard) [S27]
  • ClickStack — Dashboards (เอกสาร​ทางการ, เข้าถึง 2026-07-22) — ชนิด tile, global filter, shared time range, drilldown [S29]
  • ClickHouse blog — ClickStack SQL charting and alerting (2025) — macro $__filters / $__sourceTable สำหรับ SQL chart tile [S30]
  • ClickHouse blog — What’s new in ClickStack (November 2025) (2025-11) — trace waterfall และ filter เหลือ root span เพื่อ​สแกน [S49]

เช็กความเข้าใจ — บทที่ 6

ข้อ 1 / 3

ทำไมบท6 ถึงเป็น 'บทเดียวที่ผูกกับ sink โดยตั้งใจ' ในขณะที่ทั้งคอร์สย้ำว่า OTel portable?