ในยุคที่ระบบซอฟต์แวร์เป็น Microservices และ Distributed Systems การที่แค่ดูว่า CPU เกิน 80% จะไม่เพียงพออีกต่อไป คุณต้องเข้าใจว่า Request ของ User เดินทางผ่านระบบอย่างไร ใช้เวลาเท่าไหร่ในแต่ละ Service และ Error เกิดขึ้นที่จุดไหน นี่คือหัวใจของ Observability
บทความนี้จะสอน Observability ตั้งแต่แนวคิดพื้นฐานจนถึงการ Implement ด้วย OpenTelemetry (OTel) ซึ่งเป็นมาตรฐานเปิดที่ได้รับการยอมรับมากที่สุดในปี 2026
Observability คืออะไร?
Observability คือความสามารถในการเข้าใจสถานะภายในของระบบ (Internal State) โดยดูจาก Output ที่ระบบปล่อยออกมา โดยไม่ต้องเข้าไป Debug ใน Production โดยตรง
แนวคิดนี้มาจาก Control Theory ในวิศวกรรม: ระบบจะ Observable ได้ก็ต่อเมื่อคุณสามารถอนุมานสถานะภายในจากข้อมูลที่ออกมาจากระบบได้
Observability vs Monitoring
| ด้าน | Monitoring | Observability |
|---|---|---|
| คำถาม | "มีอะไรพังไหม?" | "ทำไมถึงพัง?" + "ตรงไหนพัง?" |
| แนวทาง | ตั้ง Alert ล่วงหน้า (Known unknowns) | สำรวจข้อมูลเพื่อหาปัญหาใหม่ (Unknown unknowns) |
| ข้อมูล | Metrics + Alerts | Traces + Metrics + Logs + Events |
| เหมาะกับ | Monolithic, ระบบง่าย | Microservices, Distributed Systems |
Three Pillars of Observability
Observability ตั้งอยู่บน 3 เสาหลัก (Three Pillars) ที่ทำงานร่วมกัน:
1. Logs (บันทึกเหตุการณ์)
Log คือข้อความที่บันทึกเหตุการณ์ที่เกิดขึ้นในระบบ ณ เวลาใดเวลาหนึ่ง เป็นข้อมูลที่ละเอียดที่สุด
# Unstructured Log (แบบเก่า)
2026-04-10 14:30:22 ERROR Failed to process order #12345
# Structured Log (แบบใหม่ — JSON)
{
"timestamp": "2026-04-10T14:30:22Z",
"level": "ERROR",
"message": "Failed to process order",
"service": "order-service",
"order_id": "12345",
"trace_id": "abc123def456",
"span_id": "span789",
"user_id": "u-5678",
"error": "PaymentGatewayTimeout"
}
trace_id เพื่อเชื่อมโยง Log กับ Trace
2. Metrics (ตัวเลขวัดผล)
Metrics คือค่าตัวเลขที่วัดได้ตามเวลา (Time-series data) เช่น จำนวน Request, Response Time, Error Rate
# Metric Types
Counter — ค่าที่เพิ่มขึ้นเรื่อยๆ (เช่น total_requests)
Gauge — ค่าที่ขึ้นลงได้ (เช่น current_memory_usage)
Histogram — กระจายตัวของค่า (เช่น request_duration_seconds)
# ตัวอย่าง Prometheus Metrics
http_requests_total{method="GET", endpoint="/api/orders", status="200"} 15234
http_request_duration_seconds_bucket{le="0.1"} 12000
http_request_duration_seconds_bucket{le="0.5"} 14500
http_request_duration_seconds_bucket{le="1.0"} 15100
3. Traces (การติดตาม Request)
Trace คือการบันทึกเส้นทางของ Request ตั้งแต่ต้นจนจบ ผ่านทุก Service ที่เกี่ยวข้อง ทำให้เห็นภาพรวมของ Distributed Transaction
# Trace ประกอบด้วย Spans
Trace ID: abc123def456
├── Span: API Gateway (12ms)
│ ├── Span: Auth Service (3ms)
│ ├── Span: Order Service (45ms)
│ │ ├── Span: Database Query (8ms)
│ │ └── Span: Payment Service (30ms)
│ │ └── Span: External Payment API (25ms)
│ └── Span: Notification Service (5ms)
Total Duration: 52ms
OpenTelemetry (OTel) คืออะไร?
OpenTelemetry คือโปรเจกต์ Open Source ภายใต้ CNCF (Cloud Native Computing Foundation) ที่เป็นมาตรฐานสำหรับการเก็บข้อมูล Observability ทั้ง Traces, Metrics และ Logs โดยรวม OpenTracing กับ OpenCensus เข้าด้วยกัน
OTel เป็นโปรเจกต์ที่มี Activity สูงเป็นอันดับ 2 ใน CNCF (รองจาก Kubernetes) และเป็นมาตรฐานที่ Vendor ทุกเจ้ารองรับ
OTel Architecture
┌────────────────────────────────────────────┐
│ Your Application │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ OTel API │ │ OTel SDK │ │ Auto- │ │
│ │ │→ │ │→ │ Instrument│ │
│ └──────────┘ └──────────┘ └──────────┘ │
└──────────────────┬─────────────────────────┘
│ OTLP (OpenTelemetry Protocol)
▼
┌─────────────────┐
│ OTel Collector │
│ ┌─────┐┌──────┐ │
│ │Recv ││Proc │ │
│ └─────┘└──────┘ │
│ ┌──────────────┐ │
│ │ Exporters │ │
│ └──────────────┘ │
└────┬───┬───┬────┘
│ │ │
┌────────┘ │ └────────┐
▼ ▼ ▼
Jaeger Prometheus Loki
(Traces) (Metrics) (Logs)
└────────┬───┘────────┘
▼
Grafana (UI)
OTel SDK: Auto-Instrumentation
วิธีที่ง่ายที่สุดในการเริ่มต้น — ไม่ต้องแก้ Code เลย:
Python
# ติดตั้ง
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install
# รัน Application พร้อม Auto-instrumentation
opentelemetry-instrument \
--service_name my-python-app \
--exporter_otlp_endpoint http://otel-collector:4317 \
python app.py
# รองรับ Framework: Flask, Django, FastAPI, SQLAlchemy, Redis, etc.
Java
# ดาวน์โหลด Java Agent
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
# รัน Application พร้อม Agent
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.service.name=my-java-app \
-Dotel.exporter.otlp.endpoint=http://otel-collector:4317 \
-jar myapp.jar
# รองรับ: Spring Boot, Quarkus, Micronaut, JDBC, Kafka, gRPC, etc.
Node.js
# ติดตั้ง
npm install @opentelemetry/auto-instrumentations-node \
@opentelemetry/sdk-node \
@opentelemetry/exporter-trace-otlp-grpc
# สร้าง tracing.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const sdk = new NodeSDK({
serviceName: 'my-node-app',
traceExporter: new OTLPTraceExporter({ url: 'http://otel-collector:4317' }),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
# รัน
node --require ./tracing.js app.js
Go
// ติดตั้ง
go get go.opentelemetry.io/otel
go get go.opentelemetry.io/otel/sdk
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
// Initialize Tracer Provider
func initTracer() func() {
ctx := context.Background()
exporter, _ := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint("otel-collector:4317"),
otlptracegrpc.WithInsecure(),
)
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("my-go-app"),
)),
)
otel.SetTracerProvider(tp)
return func() { tp.Shutdown(ctx) }
}
Manual Instrumentation
สำหรับ Business Logic ที่ต้องการ Trace เฉพาะจุด:
# Python Manual Span
from opentelemetry import trace
tracer = trace.get_tracer("order-service")
def process_order(order_id: str):
with tracer.start_as_current_span("process_order") as span:
span.set_attribute("order.id", order_id)
span.set_attribute("order.type", "standard")
# ขั้นตอนย่อย
with tracer.start_as_current_span("validate_order"):
validate(order_id)
with tracer.start_as_current_span("charge_payment") as pay_span:
result = charge(order_id)
pay_span.set_attribute("payment.method", result.method)
pay_span.set_attribute("payment.amount", result.amount)
with tracer.start_as_current_span("send_notification"):
notify(order_id)
span.set_status(trace.Status(trace.StatusCode.OK))
W3C Trace Context
OpenTelemetry ใช้ W3C Trace Context เป็นมาตรฐานในการส่ง Trace ID ข้าม Service (Context Propagation):
# HTTP Header ที่ OTel ใส่ให้อัตโนมัติ
traceparent: 00-abc123def456789-span789abc-01
tracestate: vendor1=value1,vendor2=value2
# Format: version-trace_id-parent_span_id-trace_flags
# trace_id: 32 hex characters (16 bytes)
# parent_span_id: 16 hex characters (8 bytes)
# trace_flags: 01 = sampled
OpenTelemetry Collector
OTel Collector เป็นตัวกลางที่รับ Telemetry Data จาก Application แล้วส่งต่อไป Backend ทำหน้าที่เป็น Proxy/Pipeline:
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 5s
send_batch_size: 1024
memory_limiter:
check_interval: 1s
limit_mib: 512
attributes:
actions:
- key: environment
value: production
action: upsert
exporters:
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
prometheus:
endpoint: 0.0.0.0:8889
loki:
endpoint: http://loki:3100/loki/api/v1/push
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp/jaeger]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus]
logs:
receivers: [otlp]
processors: [memory_limiter, batch, attributes]
exporters: [loki]
Backends: เก็บข้อมูลที่ไหน?
| ประเภท | Backend | จุดเด่น |
|---|---|---|
| Traces | Jaeger | Open Source, UI ดี, ค้นหา Trace ง่าย |
| Traces | Tempo (Grafana) | ต้นทุนต่ำ ใช้ Object Storage, Scale ดี |
| Traces | Zipkin | เก่าแก่ เสถียร Community ใหญ่ |
| Metrics | Prometheus | มาตรฐาน Pull-based, PromQL ทรงพลัง |
| Metrics | Mimir (Grafana) | Long-term storage, Multi-tenant |
| Logs | Loki (Grafana) | เหมือน Prometheus แต่สำหรับ Logs, ต้นทุนต่ำ |
| Logs | Elasticsearch | Full-text search, Kibana UI |
| All-in-one | Grafana Cloud | Managed service ครบจบ |
Grafana: Unified Dashboard
Grafana เป็น UI ที่เชื่อมทุกอย่างเข้าด้วยกัน สามารถดู Traces, Metrics, Logs ในหน้าจอเดียว และ Correlate ข้อมูลข้ามกันได้:
# Docker Compose สำหรับ Full Observability Stack
version: '3.8'
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
volumes:
- ./otel-config.yaml:/etc/otelcol/config.yaml
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
jaeger:
image: jaegertracing/all-in-one:latest
ports:
- "16686:16686" # Jaeger UI
- "4317" # OTLP
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
Distributed Tracing Deep Dive
Distributed Tracing เป็นส่วนที่สำคัญที่สุดของ Observability เพราะช่วยให้เห็นปัญหาที่ Metrics และ Logs เพียงอย่างเดียวจะไม่เห็น:
ตัวอย่าง: หา Bottleneck
# Trace แสดงให้เห็นว่า Payment Service ช้า
GET /api/checkout (total: 2.3s)
├── auth-service: verify-token (15ms)
├── cart-service: get-items (45ms)
├── inventory-service: check-stock (120ms)
├── payment-service: process-payment (1,800ms) ← BOTTLENECK!
│ ├── fraud-check (200ms)
│ ├── gateway-call (1,500ms) ← External API ช้า
│ └── save-transaction (100ms)
└── notification-service: send-email (50ms)
# โดย Metrics จะเห็นแค่ "checkout API ช้า 2.3s"
# แต่ Trace บอกได้ว่าช้าเพราะ Payment Gateway
SLO / SLI / Error Budget
OpenTelemetry ช่วยวัด SLI (Service Level Indicators) เพื่อกำหนด SLO (Service Level Objectives):
| คำศัพท์ | ความหมาย | ตัวอย่าง |
|---|---|---|
| SLI | ค่าที่วัดได้จริง | 99.2% ของ Request สำเร็จ |
| SLO | เป้าหมายที่ตั้งไว้ | 99.9% availability |
| SLA | สัญญากับลูกค้า | 99.5% uptime หรือคืนเงิน |
| Error Budget | Downtime ที่ยอมรับได้ | 0.1% = 43.8 นาที/เดือน |
# PromQL สำหรับ SLI
# Availability SLI
sum(rate(http_requests_total{status!~"5.."}[30d]))
/
sum(rate(http_requests_total[30d]))
# Latency SLI (p99 < 500ms)
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
Observability-Driven Development
แนวคิดสมัยใหม่ที่ว่า "เขียน Instrumentation ก่อน เขียน Code":
- Define SLOs — กำหนดเป้าหมายของระบบก่อน
- Instrument First — เพิ่ม Traces/Metrics ตั้งแต่เริ่มเขียน Code
- Test in Production — ใช้ Observability เพื่อ Validate ใน Production
- Alert on SLOs — Alert เมื่อ Error Budget ใกล้หมด ไม่ใช่ Alert ทุกอย่าง
Cost of Observability
Observability ใช้ทรัพยากรมาก — Traces โดยเฉพาะ:
กลยุทธ์ลดค่าใช้จ่าย
- Sampling — ไม่ต้องเก็บ Trace ทุก Request
- Head-based Sampling — ตัดสินใจตั้งแต่ต้น Request (เช่น เก็บ 10%)
- Tail-based Sampling — ตัดสินใจหลัง Request จบ (เก็บเฉพาะ Error หรือ Slow)
- Metrics Cardinality — ลด Label ที่ไม่จำเป็น
- Log Filtering — เก็บเฉพาะ Log ที่สำคัญ
# OTel Collector: Tail-based Sampling
processors:
tail_sampling:
decision_wait: 10s
policies:
- name: errors
type: status_code
status_code:
status_codes: [ERROR]
- name: slow-traces
type: latency
latency:
threshold_ms: 1000
- name: percentage
type: probabilistic
probabilistic:
sampling_percentage: 10
Vendor Comparison
| Platform | จุดเด่น | Pricing Model | เหมาะกับ |
|---|---|---|---|
| Datadog | All-in-one, APM ดีมาก, UI สวย | Per host + ingestion | Enterprise ที่มีงบ |
| New Relic | Free tier ใจกว้าง, Full-stack | Per GB ingested | Startup ถึง Enterprise |
| Honeycomb | Query ทรงพลัง, BubbleUp | Per event | ทีมที่เน้น Debug |
| Grafana Cloud | Open Source-based, ยืดหยุ่น | Per metric/log/trace | ทีมที่ชอบ OSS |
| Self-hosted | ฟรี ควบคุมเต็มที่ | ค่า Infra เอง | ทีมที่มี DevOps แข็ง |
สรุป
Observability ไม่ใช่แค่เรื่องของ Tool แต่เป็นวิธีคิดในการออกแบบระบบ ในปี 2026 OpenTelemetry ได้กลายเป็นมาตรฐานที่ไม่มีใครหลีกเลี่ยงได้ ไม่ว่าจะใช้ Vendor ไหน ข้อมูลที่เก็บด้วย OTel จะ Portable และไม่ถูก Lock-in
เริ่มต้นด้วย Auto-instrumentation ก่อน — ใช้เวลาไม่ถึง 10 นาทีก็ได้ Traces แรก จากนั้นค่อยเพิ่ม Custom Spans, Metrics, และ Structured Logs ตามความต้องการของทีม
