Private Docs

Monitor Log 101 — เก็บ request ไว้ดูย้อนหลังโดยไม่เก็บความลับ

request log ต่างจาก log ที่เราเขียนเองยังไง · เก็บอะไรบ้าง · ทำไมมี record แต่ไม่มี body · ของอ่อนไหวถูกปิดยังไง · และทำไมมันไม่ทำให้ API ช้าลง

อัปเดต: 2026-09-07

Monitor Log 101 — เก็บ request ไว้ดูย้อนหลังโดยไม่เก็บความลับ

เล่มนี้สำหรับคนที่เคยเขียนแต่ _logger.LogInformation(...) แล้วสงสัยว่า “request log” ที่ทีม security พูดถึง มันคนละอย่างกันยังไง · ขั้นตอนติดตั้งอยู่ที่ ติดตั้ง F

TL;DR — อ่าน 30 วิ

  • log ที่เราเขียนเองตอบว่า “โค้ดคิดอะไรอยู่” · request log ตอบว่า “ใครเรียกอะไร ได้อะไรกลับไป”
  • เก็บทั้ง metadata (ใคร · เส้นไหน · status · ใช้เวลาเท่าไหร่) และ เนื้อ request/response
  • ช่องที่แปะ [EncryptedField] ไว้จะถูกแทนด้วย *** ก่อนออกจาก service — ไม่มีเลขบัตรใครไหลไปถึงปลายทาง
  • มี record ไม่ได้แปลว่ามี body — มี 4 กรณีที่ body หายไปโดยตั้งใจ
  • ไม่ทำให้ API ช้าลง เพราะไม่ได้ส่งตอนนั้น แต่หย่อนลงคิวแล้วมีตัวส่งเบื้องหลังทยอยส่ง

1. มันคนละอย่างกับ log ที่เราเขียนเอง

_logger.LogInformation(...)request log
ใครเขียนเราเขียนเองทีละบรรทัดในโค้ดmiddleware เก็บให้อัตโนมัติ
ตอบคำถามโค้ดเดินไปถึงไหน คิดอะไรอยู่ใครเรียกอะไร ส่งอะไรมา ได้อะไรกลับ
ไปอยู่ที่ไหนstdout ของ pod แล้วหายตาม podส่งเข้า topic → log service → เก็บลงตาราง
ใช้ตอนไหนdebug ตอนพัฒนาตรวจสอบย้อนหลัง (audit) ว่าเมื่อวานใครทำอะไรไว้

⇒ อันแรกเป็นของนักพัฒนา อันหลังเป็นของทีมที่ต้องตอบคำถามว่า “ตกลงวันนั้นใครกดปุ่มนั้น”

2. เก็บอะไรบ้าง

metadata ทุกครั้งที่มี record — เส้นที่เรียก (route template ไม่ใช่ URL ที่มีค่าจริง) · method · status · ใช้เวลากี่มิลลิวินาที · correlationId ไว้ตามงานข้าม service · oid ของคนเรียก · ขนาดของ body

เนื้อ body ทั้งขาเข้าและขาออก — เก็บ หลัง ผ่านด่านและถอดรหัสแล้ว ⇒ สิ่งที่เก็บได้คือ body ที่ handler ได้รับจริง ไม่ใช่ก้อน ciphertext ที่อ่านไม่ออก

3. 🔴 มี record ≠ มี body — 4 กรณีที่เนื้อหายไปโดยตั้งใจ

กรณีทำไม
Content-Type ไม่ใช่ JSONไฟล์ดาวน์โหลด · multipart · XML ไม่ได้ตั้งใจให้เก็บ
endpoint นอกขอบเขตที่เลือกไว้ไม่มี record เลยด้วยซ้ำ ไม่ใช่แค่ไม่มี body
endpoint ที่ผนึกทั้ง body ขาออกสิ่งที่ไหลผ่านสายคือ ciphertext อ่านย้อนไม่ได้อยู่แล้ว
route อยู่ในรายการยกเว้นตั้งใจให้นับ call แต่ไม่เก็บ payload

ผลที่กระทบการวางแผนโดยตรง — ถ้าเป้าหมายคือ “เปิดดูย้อนหลังว่า response จริงคืออะไร” ต้องเลือก endpoint ที่ ไม่ได้ ผนึกทั้ง body ⇒ เส้นที่ใช้การเข้ารหัสรายฟิลด์เก็บได้ปกติ โดยค่าที่อ่อนไหวถูกปิดเป็น *** ให้อยู่แล้ว

4. ของอ่อนไหวถูกปิดยังไง — และทำไมต้องปิดที่ backend

ช่องที่แปะ [EncryptedField] (ป้ายเดียวกับที่ใช้บอกว่า field ไหนต้องเข้ารหัสตอนเดินทาง) จะถูกแทนด้วย *** ตอนประกอบ record ⇒ ไม่มีรายชื่อ field ให้ตั้งซ้ำอีกที่ แปะป้ายที่เดียวได้ทั้งสองงาน

ต้องการปิดเพิ่มนอกเหนือจากป้าย ใส่ path ของมันใน config ได้ เช่น $.customer.taxId

🔴 การปิดต้องเกิดก่อนข้อมูลออกจาก service เสมอ — ถ้าเก็บของจริงส่งไปแล้วค่อยไปปิดตอนแสดงผลที่หน้าจอ เท่ากับความลับถูกเก็บถาวรอยู่ในฐาน log แล้ว ใครเข้าถึง log ได้ก็เห็นหมด · การปิดตอนแสดงผลไม่ใช่การปิด

5. ทำไมมันไม่ทำให้ API ช้าลง

request → เก็บ metadata + body → หย่อนลงคิวในหน่วยความจำ → ตอบผู้ใช้ทันที

                          ตัวส่งเบื้องหลังทยอยส่งเข้า topic → log service
  • ผู้ใช้ไม่ต้องรอการบันทึก — response ไหลกลับทันทีที่ handler ทำงานเสร็จ
  • คิวเต็มแล้ว record ใหม่ถูกทิ้งและนับไว้ ไม่ใช่หน่วง request ให้รอที่ว่าง
  • ปลายทางล่มก็ไม่กระทบ API — คำขอยังทำงานปกติ ความล้มเหลวไปโผล่ใน log ของ service เอง

⇒ กลไกนี้ออกแบบให้ “ยอมเสีย log ดีกว่าทำให้ผู้ใช้พัง” · ถ้าต้องการรับประกันว่าไม่มี record ตกหล่นเลย นั่นเป็นคนละโจทย์ ต้องใช้ transaction ของ business ไม่ใช่กลไกนี้

6. เลือกเก็บเฉพาะบางเส้น — และ 2 วิธีที่ไม่เหมือนกัน

ค่าตั้งต้นคือ เก็บเฉพาะเส้นที่แปะป้ายไว้ (opt-in) เส้นที่ไม่แปะไม่มี record เลย

สองวิธีคัดออกที่ห้ามใช้แทนกัน

ป้าย [SkipRequestLog]รายการ ExcludedRouteTemplates
มี record ไหมไม่มีเลยมี — route, status, เวลา, ขนาด
เก็บ body ไหมไม่มี record ก็ไม่มี bodyไม่เก็บ เก็บแต่ metadata
ใช้เมื่อการมีอยู่ของ call นั้นเองคือสิ่งที่ห้ามบันทึกcall ต้องถูกนับ แต่เนื้อห้ามเก็บ

ศัพท์ที่เจอบ่อย

คำแปลเป็นภาษาคน
request logบันทึกว่าใครเรียกอะไรได้อะไรกลับ — คนละอย่างกับ log ที่เราเขียนเอง
audit trailร่องรอยที่เอาไว้ตอบย้อนหลังว่าเกิดอะไรขึ้น ใครทำ
maskปิดค่าจริงด้วย *** ก่อนบันทึกหรือก่อนส่งออก
correlation idรหัสที่ติดไปกับคำขอเดียวกันข้าม service ไว้ตามงานว่าเรื่องนี้วิ่งไปไหนบ้าง
route templateรูปแบบของเส้น เช่น users/{id} — เก็บแบบนี้แทน URL จริงเพื่อไม่ให้ค่าจริงหลุดเข้า log
queue / background senderคิวในหน่วยความจำและตัวส่งเบื้องหลัง ทำให้การบันทึกไม่หน่วงผู้ใช้
fire-and-forgetยิงแล้วไม่รอผล — ถ้าล้มเหลวก็ไม่ทำให้คำขอหลักพัง

อ่านต่อ