Sentry พบอะไร: การสังเกตการณ์ระบบ ความเสถียร และต้นทุนของการเฝ้าดูโค้ด
ข้อผิดพลาดในโปรดักชันเผยให้เห็น schema ที่ไม่ตรงกัน การกู้คืนธุรกรรมที่ล้มเหลว และ log ที่ขาดข้อมูล ทีมเล็กใช้ Sentry แก้ปัญหา backend เตรียมการมอนิเตอร์เบราว์เซอร์ และควบคุมสัญญาณรบกวนจากการแจ้งเตือนได้อย่างไร
แต่ก่อนทีมวิศวกรรมมักลงทุนกับการสังเกตการณ์ระบบมากเกินไป (ตั้งทีม SRE ทั้งทีมมาทำแดชบอร์ดที่ไม่มีใครอ่าน) หรือไม่ก็ข้ามไปเลย (เพิ่งรู้ว่าบริการล่มเมื่อลูกค้าส่งอีเมลมา) ในปี 2026 ไม่มีทางเลือกสุดโต่งแบบไหนที่ยอมรับได้ คุณต้องรู้ว่าอะไรพัง แต่ก็ต้องรู้ได้โดยไม่ต้องจ่ายราคาแบบองค์กรสำหรับผลิตภัณฑ์อิสระ
คู่มือนี้แสดงให้เห็นว่าการเชื่อมต่อ Sentry ช่วยเผยข้อผิดพลาดในโปรดักชันอย่างไร เราเปลี่ยนอะไรเพื่อตอบสนอง และตั้งค่าการมอนิเตอร์เพื่อควบคุมสัญญาณรบกวนกับการใช้งานอย่างไร นอกจากนี้ยังพิจารณาว่าเครื่องมือสังเกตการณ์ระบบกำลังเปลี่ยนไปอย่างไรสำหรับทีมวิศวกรรมขนาดเล็ก
ทำไมการแจ้งเตือนจึงพลาดข้อผิดพลาดในเบราว์เซอร์
Backend ของเราเชื่อมกับ Sentry มาตั้งแต่เดือนมีนาคม DSN อยู่ใน Google Secret Manager, secret ถูก mount เข้า Cloud Run และ sentry_sdk.init ทำงานทุกครั้งที่เริ่มระบบ อีเมลแจ้งเตือนก็ส่งมาถึง เมื่อมองจากภายนอก ทุกอย่างดูปกติดี
แต่ภายในไม่เป็นเช่นนั้น Frontend ยังไม่ได้เชื่อมกับ Sentry เลย ข้อยกเว้นในเบราว์เซอร์ ข้อผิดพลาด hydration และปัญหาการโหลด chunk จึงมองไม่เห็น อีกทั้ง backend ยังส่งข้อผิดพลาด Python ไปยังโปรเจกต์ชื่อ javascript-nextjs เพราะเป็นโปรเจกต์เดียวในองค์กร stack trace ไปรวมผิดที่ การจัดกลุ่มใช้การไม่ได้ และแดชบอร์ดกลายเป็นกระแสเหตุการณ์หลายภาษาปะปนกัน
การอุดช่องว่างนี้หมายถึงต้องแยกข้อผิดพลาดเบราว์เซอร์ออกจากข้อผิดพลาด backend ก่อน แล้วจึงจัดการบั๊กแต่ละรายการ
ข้อมูลบอกอะไร
เราดึงเหตุการณ์ข้อผิดพลาดย้อนหลัง 90 วัน รูปแบบบางอย่างโดดเด่นขึ้นมา:
เหตุขัดข้องเก่าที่แก้ตัวเองได้
ข้อผิดพลาดที่พบบ่อยที่สุดในช่วงเวลานั้นคือ Postgres StringDataRightTruncation — 16 เหตุการณ์ใน 22 วินาที — จาก endpoint สมัครสมาชิกผ่านโซเชียล สาเหตุคือ schema drift แบบคลาสสิก: คำจำกัดความ model อนุญาตให้ bio ยาว 800 ตัวอักษร แต่คอลัมน์ในฐานข้อมูลโปรดักชันยังเป็น VARCHAR(500) จาก migration ก่อนหน้า ผู้ใช้ส่ง bio ยาวกว่า 500 ตัวอักษรเล็กน้อย ชั้น Pydantic ยอมรับ แล้วคำสั่ง INSERT ก็พัง
Migration เพื่อขยายคอลัมน์เป็น 800 ถูกเขียนและ deploy ไปแล้วในเช้าวันถัดมา แต่ปัญหาต้นตอ — model กับ schema ฐานข้อมูลอาจไม่ตรงกันโดยไม่มีใครรู้จนลูกค้าเจอปัญหา — ยังเปิดค้างอยู่
เราทำสองอย่าง อย่างแรก ปรับ schema คำขอสมัครโซเชียลให้เข้มงวดขึ้น โดยกำหนด max_length ชัดเจนให้ email, display name และ token เพื่อให้ข้อมูลผิดปกติส่งกลับเป็น 422 อย่างเรียบร้อย แทนที่จะไปถึงฐานข้อมูล อย่างที่สอง ขยายสคริปต์ตรวจความสมบูรณ์ DB ให้เปรียบเทียบ information_schema.columns.character_maximum_length กับความยาวที่แอปพลิเคชันคาดไว้ หาก migration ในอนาคตลดขนาดคอลัมน์ต่ำกว่าความยาวที่ model สร้างได้ การ deploy ครั้งถัดไปจะไม่ผ่านการตรวจความสมบูรณ์
เซสชันที่ rollback ไม่ได้
ถัดลงมาในรายการเป็นเหตุการณ์ต่อเนื่อง: OperationalError: server closed the connection unexpectedly หนึ่งครั้งบน POST /v1/upload ตามด้วย PendingRollbackError จำนวนมากจากเส้นทางโค้ดเดียวกัน Postgres ตัดการเชื่อมต่อระหว่าง query ทำให้ SQLAlchemy session อยู่ในสถานะไม่ถูกต้อง แล้วบริการอัปโหลดก็พยายามบันทึกผลลัพธ์บางส่วนทับลงบน session นั้น ทุกการทำงานถัดไปใน session จึงล้มเหลวต่อเนื่อง
วิธีแก้เล็กน้อยแต่พลาดได้ง่าย เมื่อ pipeline อัปโหลดจับ exception ได้ สิ่งแรกที่ทำตอนนี้คือ db.rollback() โดยครอบด้วย try/except แยกต่างหาก เพื่อไม่ให้ rollback ที่ล้มเหลวกลบข้อผิดพลาดเดิม หลังจากนั้นตรรกะบันทึกบางส่วนจึงทำงานบน session ที่สะอาด
log ที่ลบข้อมูลของตัวเอง
ตัวจัดการ WebSocket ตอนจบเคยบันทึก err=%s พร้อม exception ที่จับได้ สำหรับ exception ส่วนใหญ่จะพิมพ์ข้อมูลที่เป็นประโยชน์ แต่ exception บางชนิดมี __str__ ที่คืนสตริงว่าง ทำให้ log แสดงแค่ err= โดยไม่มีข้อความต่อท้าย เราจึงได้รับเหตุการณ์ Sentry ที่บอกว่าการบันทึกล้มเหลว แต่ไม่ได้บอกสาเหตุ
เปลี่ยนมาใช้ logger.exception(...) และ err=%r โดยไม่มีต้นทุนเพิ่ม แต่ทำให้เหตุการณ์ในอนาคตอธิบายตัวเองได้ครบถ้วนขึ้น
สัญญาณรบกวนที่เราไม่อยากจ่ายเงินให้
รายการที่เหลือในคิวไม่ใช่บั๊ก Uvloop บางครั้งส่ง RuntimeError: unable to perform operation on <TCPTransport closed=True> เมื่อลูกค้าตัดการเชื่อมต่อระหว่าง SSL นั่นเป็นพฤติกรรมที่ถูกต้อง ไม่ใช่สิ่งที่เราแก้ได้ และเมื่อโควต้าฟรีมีข้อผิดพลาด 5,000 ครั้งต่อเดือน เหตุการณ์เหล่านี้ก็ใช้โควต้าโดยเปล่าประโยชน์ เราเพิ่มตัวกรอง before_send เพื่อตัดเหตุการณ์ที่ตรงกับรูปแบบ exception/value เฉพาะเหล่านั้น ฝั่ง frontend ก็ใช้แนวทางเดียวกันกับ ResizeObserver loop limit exceeded และสัญญาณรบกวนจากเบราว์เซอร์ลักษณะใกล้เคียงกัน
ปรับจูนสำหรับแพ็กเกจฟรี
แผน Developer ปัจจุบันของ Sentry ซึ่งตรวจสอบเมื่อวันที่ 13 ตุลาคม 2026 ให้ข้อผิดพลาด 5,000 รายการ tracing spans 5 ล้านรายการ และ replay 50 ครั้งต่อเดือนโดยไม่คิดค่าใช้จ่าย การวัด tracing ใช้ spans ไม่ใช่หน่วยประสิทธิภาพแบบเดิม โปรดตรวจสอบขีดจำกัดของแผนปัจจุบันก่อนกำหนดอัตราการสุ่มตัวอย่าง เพราะโควต้าข้อผิดพลาดไม่ได้หมายความว่าสามารถส่งสัญญาณทุกชนิดที่ SDK รองรับได้ตามต้องการ
การตั้งค่าของเรา:
tracesSampleRate: 0ทั้ง backend และ frontend การติดตามแบบกระจายมีประโยชน์จริง แต่ด้วยหน่วยประสิทธิภาพ 10K ต่อเดือน เราอาจใช้โควต้าหมดในบ่ายเดียวที่มีปัญหา สามารถเปิดด้วยอัตราสุ่มตัวอย่างที่ต่ำเมื่อจำเป็นต้องตรวจสอบเรื่องเฉพาะได้profilesSampleRate: 0บน backend ด้วยเหตุผลเดียวกัน- ปิด Session Replay บน frontend เราคงโค้ด integration ไว้ แต่ตั้งอัตราสุ่มตัวอย่างทั้งสองเป็นศูนย์ หากบั๊กร้ายแรงต้องใช้บริบท replay เพื่อแก้ไข เราสามารถปรับ
replaysOnErrorSampleRateเป็น0.05หนึ่งวันแล้วปิดกลับ - ใช้ตัวกรอง
before_sendทั้งสอง runtime เพื่อตัดเหตุการณ์ที่ทราบอยู่แล้วว่าไม่สามารถดำเนินการได้
การเปลี่ยนแปลง backend ลดสัญญาณรบกวนจากข้อผิดพลาดที่จัดการได้ ขณะที่เตรียมการตั้งค่า browser SDK เพื่อเปิดใช้งาน นี่เป็นคนละขั้นตอนกัน: มีเพียงโค้ดและค่าการสุ่มตัวอย่างยังไม่พิสูจน์ว่าเหตุการณ์จากเบราว์เซอร์ไปถึงโปรเจกต์ที่ตั้งใจไว้
ตลาดการสังเกตการณ์ระบบกำลังเปลี่ยนไปอย่างไร
เราปรับจูนสำหรับแพ็กเกจฟรีเพราะแพ็กเกจฟรีของ Sentry ค่อนข้างใจกว้างสำหรับผลิตภัณฑ์ที่ให้บริการบนโฮสต์ แต่ก็ควรเข้าใจภาพรวมด้วย เพราะเศรษฐศาสตร์ของตลาดนี้กำลังเปลี่ยนแปลง
มีบางเรื่องเปลี่ยนไปในช่วง 12 ถึง 18 เดือนที่ผ่านมา:
- มาตรฐานเปิดลดงานย้ายระบบ แต่ไม่ได้ลบงานทั้งหมด เครื่องมือวัดผลที่รองรับ OTLP อาจลดการผูกติดกับ backend มอนิเตอร์ แต่ query, dashboard, กฎแจ้งเตือน และฟีเจอร์ SDK เฉพาะผู้ให้บริการยังต้องวางแผนและทดสอบเมื่อย้าย
- การโฮสต์เองทำให้การคำนวณต้นทุนเปลี่ยนไป ตัวเลือกอย่าง GlitchTip และ ระบบที่ใช้ OTLP เป็นหลัก เป็นอีกทางเลือกสำหรับการมอนิเตอร์ เปรียบเทียบพื้นที่จัดเก็บ การสำรองข้อมูล การอัปเกรด และเวลาวิศวกรรมกับแพ็กเกจโฮสต์ ไม่ใช่เทียบแค่ค่า VM กับค่าสมัครสมาชิก
- การดีบั๊กด้วย AI ขยายเวิร์กโฟลว์ Seer ของ Sentry เสนอหาสาเหตุต้นตอและแพตช์ ขณะที่การเชื่อมต่อ MCP ที่ได้รับอนุญาตสามารถนำบริบทข้อผิดพลาดเข้าไปใน coding assistant ได้ แพตช์ที่เสนอมาเหล่านี้ยังต้องจำลองปัญหาและทดสอบ regression ก่อนปล่อย
- ควรใส่ใจข้อจำกัดแพ็กเกจพอ ๆ กับฟีเจอร์ ปริมาณข้อผิดพลาด ระยะเวลาเก็บข้อมูล การสุ่มตัวอย่าง และสิทธิ์เข้าถึงของทีมอาจกระทบค่าใช้จ่าย เริ่มจากสัญญาณที่จำเป็นและงบประมาณจำนวนเหตุการณ์ที่วัดได้ แล้วค่อยเทียบแพ็กเกจให้ตรงกับภาระงานนั้น
ณ ภาพรวมของการเผยแพร่ครั้งนี้ Sentry ครอบคลุมความต้องการมอนิเตอร์ข้อผิดพลาดเร่งด่วนของเรา การใช้ interface แบบเปิดในจุดที่ทำได้ช่วยให้ทีมมีทางเลือกเพิ่มในอนาคต แต่การย้ายผู้ให้บริการก็ยังต้องตรวจการ route โปรเจกต์ การแจ้งเตือน ข้อมูลที่เก็บไว้ และฟีเจอร์เฉพาะของผู้ให้บริการ
ตรวจสอบการ deploy ให้การมอนิเตอร์สมบูรณ์
ณ ภาพสถานะของบทความวันที่ 17 พฤษภาคม 2026 ยังมีงาน deploy สองข้อที่ค้างอยู่:
- สร้างโปรเจกต์ Sentry สำหรับ Python แยกต่างหาก เพื่อไม่ให้ข้อผิดพลาด backend ถูกบันทึกใต้
javascript-nextjsโดยจะสลับ DSN ในรายการ Secret Manager เดียวกับที่ backend อ่านอยู่แล้ว - ใส่ DSN ของ frontend ในการตั้งค่าโปรเจกต์ Vercel เพื่อให้เริ่มส่งข้อผิดพลาดฝั่ง client โค้ดมีอยู่แล้วและเริ่มทำงานตามเงื่อนไข มีเพียง environment variable ที่ยังขาด
สำหรับการนำระบบมอนิเตอร์ขึ้นใช้ทุกครั้ง ให้ส่งเหตุการณ์ทดสอบที่ควบคุมได้จากแต่ละ runtime แล้วตรวจโปรเจกต์ environment การจัดกลุ่ม และบริบท ก่อนจะถือว่าการเชื่อมต่อเสร็จสมบูรณ์ การแยกโปรเจกต์และการเพิ่ม environment variable เป็นขั้นตอนตั้งค่า ไม่ใช่หลักฐานว่าเหตุการณ์ส่งถึงแล้ว
ทำไมเรื่องนี้จึงสำคัญ
เมื่อมองไม่เห็นสิ่งที่พัง คุณอาจลงทุนเกินจำเป็นกับโค้ดป้องกัน หรือทุ่มน้อยเกินไปแล้วปล่อยบั๊ก ทั้งสองอย่างมีต้นทุน การสังเกตการณ์ระบบที่ประหยัดและเชื่อถือได้ช่วยให้ทีมเล็กทำงานได้เหมือนทีมใหญ่ขึ้น: พบ regression ก่อนลูกค้าสังเกต แก้ไขโดยไม่ต้องเดา และใช้เวลาวิศวกรรมไปกับการสร้างสิ่งใหม่
บทเรียนที่นำไปใช้ได้คือ ต้องตรวจสอบทั้งสองฝั่ง: การแก้ไขในแอปพลิเคชันและเส้นทางมอนิเตอร์ที่ควรเปิดเผย regression ครั้งถัดไป