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

Application Metrics

log และ trace ให้รายละเอียดระดับราย request เหมาะมากตอนที่คุณรู้แล้วว่ามีอะไรผิด แล้วอยากเจาะดู request ตัวนั้น แต่ตอบไม่ได้เลยกับงานปฏิบัติการอีกครึ่งหนึ่งที่ถามว่า ตอนนี้ระบบโดยรวมยังแข็งแรงอยู่ไหม คุณไม่มีทางไล่ดู log ล้านบรรทัดต่อนาทีเพื่อสังเกตว่า error rate เพิ่งเพิ่มเป็นสองเท่า และเปิด trace ทีละอันก็ไม่ช่วยให้รู้ว่า p99 latency ทั้ง fleet ค่อย ๆ ไต่ขึ้นตลอดบ่ายหรือเปล่า สิ่งที่คุณต้องการคือบทสรุปที่พอดีหน้าจอเดียว และตัวเลขที่เครื่องเอาไปเทียบกับ threshold ได้

สัญญาณระดับรายละเอียดเอามา aggregate ได้ไม่ถูกเลย การนับ error ด้วยการ tail log ทั้งช้าและเปราะบาง การคำนวณ latency percentile ข้ามทุก replica ด้วยการอ่าน trace ก็แพงและได้คำตอบหลังเหตุการณ์จบไปแล้ว ที่สำคัญคุณต้องการมากกว่า snapshot ณ ปัจจุบัน คุณต้องการ trend ตามเวลา เพื่อให้เห็นการเสื่อมสภาพตั้งแต่ยังก่อตัว แล้ว alert ก่อน ที่จะกลายเป็นเหตุขัดข้อง ไม่ใช่มาอ่านย้อนหลังทีหลัง

แล้วคุณจะผลิตบทสรุปเชิงตัวเลขที่ราคาถูก อัปเดตอย่างต่อเนื่องของพฤติกรรมบริการของคุณได้อย่างไร — request rate, errors, latency, การใช้ทรัพยากร — ที่ dashboard สามารถวาดกราฟและระบบ alerting สามารถเฝ้าดูได้

Application metrics คือค่าวัดเชิงตัวเลขที่ service ของคุณเก็บไว้ในหน่วยความจำแล้วเปิดให้ระบบ monitoring มาดึงไป มีสามรูปแบบหลัก:

  • Counter — ค่าที่ขึ้นได้อย่างเดียว หรือรีเซ็ตเป็นศูนย์ตอน restart เช่น จำนวน request ที่ให้บริการทั้งหมด หรือจำนวน error ทั้งหมด สิ่งที่คุณเอาไปวาดกราฟคือ อัตรา การเปลี่ยนแปลง
  • Gauge — ค่าที่ขึ้นและลง: คำขอที่ in-flight ปัจจุบัน, ความลึกของ queue, ขนาดของ connection pool, หน่วยความจำที่ใช้อยู่
  • Histogram — การกระจายของค่าที่วัดได้ จัดลงถังตามช่วง เช่น ระยะเวลาของ request หรือขนาด payload แล้วคำนวณ percentile อย่าง p50, p95 และ p99 ออกมา ซึ่งบอกความจริงได้ตรงกว่าค่าเฉลี่ยมาก

รูปแบบที่ใช้กันทั่วไปคือให้แต่ละ service เปิดค่า metric ปัจจุบันไว้ที่ endpoint หนึ่ง เช่น /metrics แล้วให้ scraper มา poll เป็นช่วง ๆ เก็บทุกค่าที่อ่านได้เป็น time series ลง metrics database ที่เป็นตัวป้อน dashboard และ alert อีกที ส่วนคำถามว่า metric ไหน สำคัญ มีสอง framework สั้น ๆ ช่วยตอบ ได้แก่ RED คือ Rate, Errors, Duration สำหรับ service ที่ขับเคลื่อนด้วย request และ USE คือ Utilization, Saturation, Errors สำหรับทรัพยากรอย่าง CPU, memory และ queue

flowchart LR
  subgraph App[Service replica]
    CODE[Request handler] -->|inc / observe| REG[In-memory registry]
    REG --> EP[/metrics endpoint]
  end
  SCRAPER[Scraper] -->|poll every Ns| EP
  SCRAPER --> TSDB[(Time-series DB)]
  TSDB --> DASH[Dashboards]
  TSDB --> ALERT[Alert rules]
service อัปเดต metric ในหน่วยความจำแล้วเปิดออกมา scraper มา poll และเก็บเป็น time series ที่ป้อนให้ dashboard และ alert

สองอย่างที่คุณจะหยิบใช้บ่อยที่สุดคือ เพิ่มค่า counter เมื่อ request ทำเสร็จพร้อมติด label ตามผลลัพธ์ และบันทึก latency ของ request นั้นลง histogram ตัวอย่างข้างล่างใช้ client สไตล์ Prometheus ที่เป็น pattern มาตรฐานโดยพฤตินัยในทุกภาษา

import { Counter, Histogram } from 'prom-client';
const requests = new Counter({
name: 'http_requests_total',
help: 'Total HTTP requests',
labelNames: ['method', 'route', 'status'],
});
const latency = new Histogram({
name: 'http_request_duration_seconds',
help: 'Request latency in seconds',
labelNames: ['route'],
buckets: [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5],
});
app.use((req, res, next) => {
const stop = latency.startTimer({ route: req.path });
res.on('finish', () => {
requests.inc({ method: req.method, route: req.path, status: res.statusCode });
stop();
});
next();
});

สิ่งที่คุณได้รับ:

  • สุขภาพของ fleet บนหน้าจอเดียว กราฟ rate, errors และ duration สรุปคำขอนับพันข้ามทุก replica ลงเป็นไม่กี่เส้นที่คุณอ่านได้ในพริบตา
  • alert ได้ก่อนผู้ใช้บ่น เพราะ metric เป็น time series ต่อเนื่อง คุณจึงตั้ง alert บน error rate ที่ไต่ขึ้นหรือ p99 ที่ข้าม threshold ได้ แล้วเข้าไปแก้ก่อนที่จะบานปลายเป็นเหตุขัดข้อง
  • ราคาถูกและเปิดอยู่ตลอด counter และ histogram แทบไม่มีต้นทุนในการอัปเดตในหน่วยความจำ และถูกเก็บด้วยการ poll ดังนั้น overhead จึงคงที่เมื่อทราฟฟิกโตขึ้น — ไม่เหมือนการ log ทุกคำขอ

สิ่งที่คุณต้องจ่าย:

  • Cardinality เป็นกับดัก ทุกการผสมที่แตกต่างกันของค่า label เป็น time series แยกต่างหาก การใส่ user id หรือ raw URL ที่มี id ลงใน label สามารถระเบิดเป็น time series นับล้านและทำให้ metrics store ล่ม ให้ label เป็น low-cardinality — method, route template, status class
  • ค่า aggregate กลบรายตัว metric บอกได้ว่า error rate เพิ่มขึ้น แต่บอกไม่ได้ว่า request ไหน ล้มเหลวและเพราะอะไร คุณยังต้องใช้ log และ trace เจาะลงไปอยู่ดี พูดง่าย ๆ คือ metric ชี้จุด ส่วนสัญญาณอื่นเป็นตัวอธิบาย
  • Percentile ต้องการ histogram ไม่ใช่ค่าเฉลี่ย latency เฉลี่ยที่ดูดีสามารถซ่อน p99 ที่แย่ได้ เลือก histogram bucket ที่ครอบคลุม service-level objective ของคุณ ไม่เช่นนั้น percentile ของคุณจะหยาบเกินไปจนไร้ประโยชน์
  • Distributed Tracing — เมื่อ latency metric พุ่ง trace จะชี้ให้เห็นว่า hop ไหนเป็นต้นเหตุ
  • Health Check API — health check ตอบได้แค่ผ่านหรือไม่ผ่าน ส่วน metric เผยให้เห็นการเสื่อมถอยแบบค่อยเป็นค่อยไปที่เกิดขึ้นก่อนหน้านั้น
ข้อดีข้อแลกเปลี่ยน
รู้ health ของระบบแบบ real-time โดยไม่ต้อง tail logต้องเลือก metric ที่ใช่ — metric มากเกินทำให้ noise สูง
trigger alert เมื่อ metric เกิน threshold ได้อัตโนมัติcardinality สูง (เช่น tag ด้วย user_id) ทำให้ storage พุ่ง
ดู trend ข้ามเวลา — เห็น degradation ก่อนที่จะพังmetric ไม่บอก “ทำไม” — ต้องใช้ log และ trace ช่วย
ใช้ตัดสินใจ capacity planning และ scalinginstrumenting code เพิ่ม complexity เล็กน้อย

Metric Explosion (High Cardinality) — ใช้ dynamic value เป็น label/tag อาการ:

  • label ด้วย user_id, request_id, หรือ url_path แบบ raw
  • Prometheus หรือ metric store ใช้ memory พุ่งสูง
  • query ช้ามากเพราะ series count ระเบิด

Vanity Metrics — track metric ที่ไม่ได้บอกอะไรเกี่ยวกับ health จริง ๆ อาการ:

  • นับ request count แต่ไม่นับ error rate หรือ latency percentile
  • dashboard สวยแต่ไม่ได้ช่วย debug incident

💡 ตัวอย่างจากของจริง

Netflix:

  • ใช้ Atlas เป็น in-house time-series metric system
  • track ล้าน metric series ต่อวินาที
  • RED method: Rate, Errors, Duration สำหรับทุก service

Airbnb:

  • ใช้ Prometheus + Grafana
  • SLO-based alerting — alert เมื่อ error budget หมด ไม่ใช่เมื่อ metric เกิน threshold เดี่ยว ๆ
ควรใช้ metric ชนิดใดกับ request latency เพื่อให้คำนวณ p95 และ p99 ได้
สัญญาณแบบ RED ย่อมาจากอะไร
ทำไมการใส่ user id ลงใน metric label จึงอันตราย