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

Observability

อนุมาน​สถานะ​ภายใน​ของ​ระบบ​จาก​ผลลัพธ์​ภายนอก​ที่​มัน​ปล่อย​ออก​มา

Observability เป็น​แนว​ปฏิบัติ​ใน​แวดวง​วิศวกรรม​ซอฟต์แวร์​และ DevOps ที่​ว่าด้วย​แนวทาง​เชิง​ระบบ​ใน​การ​ทำความ​เข้าใจ​ว่า สถานะ​ภายใน​ของ​ระบบ​สามารถ​อนุมาน​ได้​ดี​เพียง​ใด​จาก​ผลลัพธ์​ภายนอก​ที่​มัน​ปล่อย​ออก​มา คำ​นี้​ยืม​มา​จาก control theory ซึ่ง “observability” ของ​ระบบ​วัด​ว่า​เรา​สามารถ​ระบุ​สถานะ​ภายใน​ของ​ระบบ​ได้​ดี​เพียง​ใด​จาก​ค่า output ที่​สังเกต​ได้ ใน​บริบท​ซอฟต์แวร์ โดย​เฉพาะ​ระบบ​แบบ​กระจาย (distributed systems) มัน​หมาย​ถึง​ความ​สามารถ​ใน​การ​รวบรวม​ข้อมูล​เกี่ยว​กับ​การ​ทำงาน​ของ​โปรแกรม สถานะ​ภายใน​ของ​แต่ละ module และ​การ​สื่อสาร​ระหว่าง​คอมโพเนนต์ ผ่าน​การ​เก็บ เชื่อม​โยง และ​วิเคราะห์​ข้อมูล telemetry จาก logs, metrics และ traces

เป้าหมาย​หนึ่ง​ของ observability คือ​การ​ลด​ปริมาณ “ความ​รู้​ล่วงหน้า” (prior knowledge) ที่​จำเป็นต่อ​การ debug ปัญหา​ให้​เหลือ​น้อย​ที่สุด — ทีม​ที่​มี​แค่ monitoring แบบ​เดิม​ต้อง​พึ่ง​ชุด dashboard/alert ที่​กำหนด​ไว้​ล่วงหน้า​และ​ความ​เชี่ยวชาญ​เฉพาะ​ทาง ใน​ขณะ​ที่​ทีม​ที่​มี observability ที่​ดี​สามารถ​ตั้ง​คำถาม​ใหม่​กับ​ระบบ​ได้​แบบ ad-hoc โดย​ไม่​ต้อง deploy code ใหม่​เพื่อ​เก็บ​ข้อมูล​เพิ่ม นี่​คือ​ความ​แตก​ต่าง​สำคัญ​ระหว่าง monitoring (เฝ้า​ดู​สิ่ง​ที่​รู้​ล่วงหน้า​ว่า​จะ​พัง) กับ observability (สำรวจ​สิ่ง​ที่​ไม่​เคย​คาด​คิด​มา​ก่อน) — observability จึง​เป็น​รากฐาน​ของ Site Reliability Engineering (SRE) เพราะ​มัน​คือ​ขั้นตอน​แรก​ใน​การ​วิเคราะห์​และ​แก้​ปัญหา service outage

  • Logs: บันทึก​เหตุการณ์​ราย​ชิ้น​ที่​ประทับ​เวลา (time-stamped) ให้​รายละเอียด​ว่า​ระบบ​ทำ​อะไร​ใน​แต่ละ​ช่วง รวม​ถึง error, warning และ​ข้อความ informational จำเป็นต่อ​การ debug และ​เข้าใจ​ลำดับ​เหตุการณ์​ที่​นำ​ไป​สู่​สถานะ​หนึ่ง อาจ​เป็น​แบบ structured (เช่น JSON) หรือ unstructured (ข้อความ​ธรรมดา)
  • Metrics: ค่าตัวเลข​ที่​แทน​สถานะ​หรือ​ประสิทธิภาพ​ของ​ระบบ​ตาม​เวลา เก็บ​เป็น time series ที่​มี timestamp, ชื่อ, ค่า และ label ประกอบ มัก​ถูก aggregate เพื่อ​ให้​ภาพ​รวม​ระดับ​สูง​ของ​สุขภาพ​ระบบ เช่น CPU, memory, request rate, error rate, response time — สำคัญ​ต่อ​การ​หา​แนวโน้ม ตรวจ​จับ anomaly และ​จุด​ชนวน alert
  • Traces: ติดตาม​การ​เดินทาง​ของ request หนึ่ง​รายการ​ขณะ​มัน​วิ่ง​ผ่าน component และ service ต่าง ๆ ใน​ระบบ​แบบ​กระจาย ให้​แผนที่​เส้นทาง​พร้อม​ข้อมูล​เวลา​ใน​แต่ละ​ช่วง (span) ช่วย​ชี้​จุด​คอ​ขวด ความ​ล่าช้า และ dependency ระหว่าง​บริการ มี​ค่า​ยิ่ง​ใน microservices ที่ request หนึ่ง​อาจ​ไหล​ผ่าน​สิบ​กว่า​บริการ

ทั้ง​สาม​เสา​นี้​เสริม​กัน: metrics ใช้​เตือน​ทีม​ว่า​มี​ปัญหา​เกิด​ขึ้น traces แสดง​เส้น​ทางการ​ทำงาน​ที่​ทำให้​เกิด​ปัญหา​นั้น และ logs ให้​บริบท​รายละเอียด​ที่​จำเป็น​สำหรับ​แก้ไข — การ​มี​ครบ​ทั้ง​สาม​เสา​และ “เชื่อม​โยง” (correlate) เข้า​ด้วย​กัน​ได้ (เช่น​ผ่าน trace ID เดียวกัน) คือ​สิ่ง​ที่​ทำให้ observability เกิด​ขึ้น​จริง ไม่ใช่​แค่​การ​มี log จำนวน​มาก​เฉย ๆ

แนว​ปฏิบัติ​หลัก​ใน​การ​สร้าง observability ให้​ระบบ​มี 3 ขั้นตอน​ต่อ​เนื่อง​กัน

  1. Instrumentation — ใส่ code เพื่อ​เก็บ telemetry ณ จุด​ที่​มี​ความหมาย​ทาง​ธุรกิจ​หรือ​ทาง​เทคนิค เช่น เริ่ม/จบ operation, จับ exception, นับ​จำนวน request ควร​ทำ​ทั้ง​ระดับ infrastructure (CPU, memory, GC) และ​ระดับ application/domain (เช่น “จำนวน​คำ​สั่ง​ซื้อ​ที่​ล้มเหลว”)
  2. Correlation — เชื่อม logs, metrics และ traces เข้า​ด้วย​กัน​ด้วย identifier ร่วม เช่น trace_id/correlation_id เพื่อ​ให้​เมื่อ alert แจ้ง​เตือน​จาก metric หนึ่ง​ตัว วิศวกร​สามารถ​กระโดด​ไป​ดู trace และ log ที่​เกี่ยวข้อง​ได้​ทันที​โดย​ไม่​ต้อง​เดา
  3. Visualization & Alerting — ใช้ dashboard แสดง​ผล​รวม ตั้ง alert บน threshold หรือ anomaly และ​ทำ SLO/error-budget เพื่อ​ให้​ทีม​รู้​ว่า​เมื่อไร​ควร​เข้าไป​ดู​ระบบ

มาตรฐาน​เปิด​ที่​ครอง​พื้นที่​นี้​ใน​ปัจจุบัน​คือ OpenTelemetry (OTel) ซึ่ง​รวม SDK/API สำหรับ​สร้าง logs, metrics, traces แบบ vendor-neutral — instrument code ครั้ง​เดียว แล้ว​ส่ง​ออก​ไป​ยัง backend ใด​ก็ได้ (Prometheus, Jaeger, Grafana, Application Insights ฯลฯ) ผ่าน exporter ที่​ตั้ง​ค่า​ได้ ทำให้​ทีม​ไม่​ถูก​ผูก​ติด​กับ​ผู้​ให้​บริการ​ราย​ใด​ราย​หนึ่ง (avoid vendor lock-in)

Pete Hodgson ใน​บทความ Domain-Oriented Observability (เผยแพร่​บน​เว็บไซต์​ของ Martin Fowler) ชี้​ปัญหา​ที่​พบ​บ่อย: การ instrument code ตรง ๆ ด้วย​การ​เรียก logger/metrics client กระจัดกระจาย​อยู่​ทั่ว domain logic ทำให้ code อ่าน​ยาก​และ​ปน​กัน​ระหว่าง​ตรรกะ​ทาง​ธุรกิจ​กับ plumbing ทาง​เทคนิค ทาง​แก้​คือ pattern ที่​เรียก​ว่า Domain Probe — สร้าง high-level API ที่​มีชื่อ method สื่อ​ความหมาย​ทาง​ธุรกิจ (เช่น OrderProbe.DiscountApplied(...)) ห่อ​หุ้ม logger/metrics/tracer ไว้​ภายใน ทำให้ domain class ไม่​ต้อง​รู้จัก infrastructure ของ​การ observe เลย code domain จึง​ยัง​คง​อ่าน​ง่าย​และ​ทดสอบ​ง่าย ใน​ขณะ​ที่​ทีม observability ปรับ implementation เบื้องหลัง probe ได้​อย่าง​อิสระ

ตัวอย่าง​การ instrument ด้วย System.Diagnostics.ActivitySource (ซึ่ง​เป็น implementation ของ Tracer ใน OpenTelemetry สำหรับ .NET) ร่วม​กับ ILogger และ Domain Probe pattern เพื่อ​ไม่​ให้ code domain ปน​กับ plumbing:

// DomainProbe.cs — ห่อหุ้มรายละเอียดการ instrument ไว้เบื้องหลัง API ที่สื่อความหมายทางธุรกิจ
public sealed class OrderProbe
{
private static readonly ActivitySource ActivitySource = new("Store.Orders");
private readonly ILogger<OrderProbe> _logger;
private readonly Counter<long> _discountAppliedCounter;
public OrderProbe(ILogger<OrderProbe> logger, Meter meter)
{
_logger = logger;
_discountAppliedCounter = meter.CreateCounter<long>("orders.discount_applied");
}
// ชื่อ method สื่อความหมายทางธุรกิจ ไม่ใช่ "LogInfo" หรือ "TrackEvent" ทั่วไป
public void DiscountApplied(string orderId, string couponCode, decimal amount)
{
using var activity = ActivitySource.StartActivity("Order.DiscountApplied");
activity?.SetTag("order.id", orderId);
activity?.SetTag("coupon.code", couponCode);
_discountAppliedCounter.Add(1, new KeyValuePair<string, object?>("coupon", couponCode));
_logger.LogInformation(
"Applied discount {Amount} to order {OrderId} using coupon {Coupon}",
amount, orderId, couponCode);
}
}
// OrderService.cs — domain logic เรียก probe แทนการยุ่งกับ logger/tracer โดยตรง
public class OrderService
{
private readonly OrderProbe _probe;
public OrderService(OrderProbe probe) => _probe = probe;
public void ApplyCoupon(Order order, string couponCode)
{
var discount = order.CalculateDiscount(couponCode);
order.Total -= discount;
// domain logic อ่านง่าย ไม่มี noise ของ instrumentation ปนอยู่
_probe.DiscountApplied(order.Id, couponCode, discount);
}
}

การ​ตั้ง​ค่า​ฝั่ง host เพื่อ export traces/metrics ผ่าน OpenTelemetry ไป​ยัง OTLP collector (เช่น Jaeger หรือ Grafana Tempo):

Program.cs
builder.Services.AddOpenTelemetry()
.ConfigureResource(r => r.AddService("Store.Orders"))
.WithTracing(tracing => tracing
.AddSource("Store.Orders") // custom ActivitySource ของเรา
.AddAspNetCoreInstrumentation() // trace อัตโนมัติของ incoming HTTP request
.AddHttpClientInstrumentation() // trace อัตโนมัติของ outgoing call
.AddOtlpExporter())
.WithMetrics(metrics => metrics
.AddMeter("Store.Orders")
.AddAspNetCoreInstrumentation()
.AddOtlpExporter());

แผนภาพ​ต่อ​ไป​นี้​แสดง​ว่า request หนึ่ง​รายการ​ก่อ​ให้​เกิด telemetry ทั้ง​สาม​ชนิด​อย่างไร และ​ถูก​เชื่อม​โยง​กัน​ด้วย correlation ID เดียวกัน​ก่อน​ไหล​ไป​ยัง backend สำหรับ visualize:

flowchart LR
    Request[Incoming Request] --> App[Application Code]
    App --> Logs[Structured Logs]
    App --> Metrics[Metrics Counters]
    App --> Traces[Trace Spans]
    Logs --> Correlate[Correlation via trace id]
    Metrics --> Correlate
    Traces --> Correlate
    Correlate --> Backend[Observability Backend]
    Backend --> Dashboard[Dashboards and Alerts]

ประโยชน์

  • ลด​เวลา​ใน​การ​หา​สาเหตุ​ปัญหา (MTTR) เพราะ​วิศวกร​ตั้ง​คำถาม​ใหม่​กับ​ระบบ​ได้​ทันที​โดย​ไม่​ต้อง deploy code เก็บ​ข้อมูล​เพิ่ม
  • เห็น​ภาพ​รวม end-to-end ของ​ระบบ​แบบ​กระจาย ซึ่ง​เป็น​สิ่ง​ที่​การ​ดู log ที​ละ​เครื่อง​ทำ​ไม่​ได้
  • เมื่อ​ทำ​แบบ domain-oriented (Domain Probe) จะ​ได้ metric ที่​สะท้อน​เป้าหมาย​ทาง​ธุรกิจ​จริง เช่น cart abandonment rate ไม่ใช่​แค่ CPU/memory ที่​เป็น technical metric ทั่วไป
  • เป็น​รากฐาน​ให้ SRE ตั้ง SLO/error budget และ​ทำ incident response ได้​อย่าง​มี​ข้อมูล​รองรับ

ข้อ​ควร​ระวัง

  • Over-instrumentation ทำให้​เกิด noise ล้น​ระบบ (log/metric cardinality สูง​เกิน​ไป) ซึ่ง​เพิ่ม​ต้นทุน​เก็บ​ข้อมูล​และ​ทำให้​หา​สัญญาณ​จริง​ยาก​ขึ้น ควร instrument อย่าง​มี​เป้าหมาย ไม่ใช่​ใส่​ทุก​จุด
  • ถ้า​ไม่มี correlation ID เชื่อม logs/metrics/traces เข้า​ด้วย​กัน การ​มี​ข้อมูล​ทั้ง​สาม​ชนิด​แยก​กัน​ก็​ยัง​ไม่ใช่ observability ที่แท้​จริง
  • การ instrument แบบ​เรียก logger/tracer ตรง ๆ ใน code domain ทำให้ code อ่าน​ยาก​และ​ทดสอบ​ยาก — ควร​พิจารณา​แยก​ผ่าน pattern อย่าง Domain Probe
  • Telemetry data มัก​มี​ข้อมูล​อ่อนไหว (PII) ปน​อยู่​โดย​ไม่​ตั้งใจ ต้อง​ระวัง​เรื่อง data governance และ redaction ก่อน​ส่ง​ออก​ไป​ยัง backend ภายนอก
  • เครื่องมือ observability มี​ต้นทุน​ทั้ง​ด้าน license/SaaS และ storage สำหรับ high-cardinality metrics/traces ควร​วางแผน retention policy ตั้งแต่​ต้น