📡 Obs · Zero→Hero
หน้าแรก/🪵 3 · Logs/พื้นฐาน Logging
📖 บทเรียน⏱ ~13 นาที

พื้นฐาน Logging

Log เป็นเสาที่คุณมีอยู่แล้ว (ELK) แต่ก่อนจะทบทวนสถาปัตยกรรม เรามาปูพื้นฐานว่า log ที่ดี หน้าตาเป็นยังไง เพราะ ELK ที่เก่งแค่ไหนก็ช่วยไม่ได้ถ้า log ต้นทางมั่ว

Log Levels — จัดระดับความสำคัญ

ทุก log ควรมี level เพื่อบอกความรุนแรง และให้ filter ได้ ระดับมาตรฐาน (เรียงจากเบาไปหนัก):

Level ใช้เมื่อ ตัวอย่าง ควร alert?
TRACE ละเอียดยิบ debug ลึก "เข้าฟังก์ชัน X ด้วย arg=..."
DEBUG ข้อมูลช่วย dev ตอน debug "cache miss สำหรับ key=..."
INFO เหตุการณ์ปกติที่ควรรู้ "user 889 สั่งซื้อสำเร็จ"
WARN ผิดปกติแต่ยังไปต่อได้ "retry ครั้งที่ 2, DB ตอบช้า" บางที
ERROR งานนี้ล้มเหลว "ชำระเงินไม่สำเร็จ: card declined" ✅ มักใช่
FATAL ระบบไปต่อไม่ได้ ต้องตาย "ต่อ DB ไม่ได้ตอน startup" ✅ ใช่

ตั้งค่า level ที่ปล่อยออกได้ตาม environment: production ปกติปล่อย INFO ขึ้นไป (DEBUG/TRACE ปิดไว้เพราะเยอะและแพง) แต่เปิด DEBUG ชั่วคราวได้เมื่อต้อง debug ปัญหาเฉพาะ

อย่าใช้ level มั่ว — log ที่ตั้งเป็น ERROR ทั้งที่ไม่ใช่ error จริง จะสร้าง "ERROR fatigue" แบบเดียวกับ alert fatigue (บทที่ 14) คนจะเลิกเชื่อ ERROR log

Structured Logging — หัวใจของ log ที่ค้นได้

นี่คือเรื่องสำคัญที่สุดของบทนี้ เทียบ log 2 แบบ:

❌ Unstructured (ข้อความดิบ) — คนอ่านออก แต่เครื่อง parse ยาก:

2026-08-20 10:30:04 ERROR user 889 failed to pay order 5512 amount 1500 card declined

✅ Structured (JSON) — เครื่อง query ได้ทันที:

{
  "timestamp": "2026-08-20T10:30:04Z",
  "level": "error",
  "service": "payment-api",
  "event": "payment_failed",
  "user_id": "889",
  "order_id": "5512",
  "amount": 1500,
  "reason": "card_declined"
}

ทำไม structured ชนะขาด:

  • ค้นได้แม่นยำ — ใน Kibana ค้น user_id: "889" AND level: "error" ได้ทันที ไม่ต้อง regex มั่ว
  • aggregate ได้ — นับ payment_failed แยกตาม reason ได้เลย
  • ไม่พังเมื่อข้อความมี format แปลก — ไม่ต้องพึ่ง grok pattern ที่เปราะ

นี่คือเหตุผลที่ lab ELK ของคุณ (lab-05-structured-nestjs) ขยับมาใช้ JSON logging — และทำไม logstash.conf ใน lab นั้นใช้ json filter แทน grok ที่ซับซ้อน โครงสร้างที่ต้นทางดี ทำให้ปลายทางง่ายลงมหาศาล

Correlation ID — ร้อย log ของ request เดียวเข้าด้วยกัน

ในระบบที่ 1 request วิ่งผ่านหลาย service (หรือหลาย log line) เราต้องการ ตัวเชื่อม เพื่อตามรอย request เดียวได้:

{ "trace_id": "a1b2c3", "service": "gateway",  "msg": "รับ request" }
{ "trace_id": "a1b2c3", "service": "auth",     "msg": "ยืนยันตัวตนผ่าน" }
{ "trace_id": "a1b2c3", "service": "payment",  "msg": "charge failed", "level": "error" }

ค้น trace_id: "a1b2c3" ก็เห็น ทั้งเส้นทาง ของ request นั้นเรียงกัน — นี่คือสะพานเชื่อม log เข้ากับ traces (Module 4) และเป็นหัวใจของการ correlate 3 pillars (บทที่ 50)

สร้าง trace_id ที่ edge (gateway/first service) แล้วส่งต่อผ่าน HTTP header (traceparent) ให้ทุก service log ด้วย id เดียวกัน — OpenTelemetry (บทที่ 41) ทำเรื่องนี้ให้อัตโนมัติ

ของที่ "ห้าม" log เด็ดขาด

อย่า log ข้อมูลอ่อนไหว — เพราะ log มักถูกเก็บนาน, ส่งข้าม network, และเข้าถึงได้หลายคน ห้าม log:

  • รหัสผ่าน, token, API key, session cookie
  • เลขบัตรเครดิตเต็ม, CVV
  • PII ตามกฎหมาย (เลขบัตรประชาชน, ข้อมูลสุขภาพ) — ระวัง PDPA/GDPR
  • ข้อมูลส่วนตัวที่ไม่จำเป็น

ถ้าจำเป็นต้องอ้างถึง ให้ mask (****1234) หรือ hash แทน การเผลอ log ของพวกนี้เป็นสาเหตุ data breach ที่พบบ่อยมาก

ปริมาณและต้นทุน — log แพงกว่าที่คิด

จำจากบทที่ 01: log ใหญ่และแพงกว่า metric เป็นพันเท่า ดังนั้น:

  • อย่า log ทุกอย่างใน loop — log ในลูปที่วิ่งล้านรอบ = ระเบิด volume + ค่าใช้จ่าย
  • Sampling — สำหรับ event ที่เยอะมาก log แค่บางส่วน (เช่น 1%) ก็พอเห็น pattern
  • ตั้ง retention — log ปกติเก็บ 7–30 วันก็พอ (ต่างจาก metric ที่เก็บเป็นปีได้เพราะเล็ก)
  • แยก level — เก็บ ERROR นานกว่า DEBUG

สรุป

  • ใช้ log levels ให้ถูก (INFO ขึ้นไปใน prod, ERROR/FATAL คือของที่ควร alert)
  • Structured logging (JSON) คือหัวใจ — ค้นได้ aggregate ได้ ไม่ต้องพึ่ง grok เปราะๆ
  • ใส่ correlation/trace ID เพื่อร้อย log ของ request เดียว (สะพานสู่ traces)
  • ห้าม log ข้อมูลอ่อนไหว (password, token, PII) — mask/hash แทน
  • log แพง — คุม volume ด้วย sampling + retention

บทหน้า: ทบทวน สถาปัตยกรรม ELK และโยงเข้ากับ lab 01–06 ที่คุณทำมาให้เป็นภาพเดียว