Traces, Spans, and the Service Map
ไอเดียหลักในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียหลักในหนึ่งประโยค”trace คือ journey ทั้งหมดของ request หนึ่งตัวตั้งแต่ต้นจนจบ ประกอบขึ้นจาก span ที่เรียงตัวเป็น tree แบบ parent/child และ Datadog สร้าง Service Map ขึ้นมาโดยอ่าน structure ของ tree นั้นตรง ๆ จาก traffic จริง ไม่ใช่จากไดอะแกรมที่ใครวาดไว้เอง
Span: หน่วยย่อยของ trace
หัวข้อที่มีชื่อว่า “Span: หน่วยย่อยของ trace”span แทน work หนึ่งหน่วย — HTTP request หนึ่งตัว, database query หนึ่งครั้ง, function call หนึ่งครั้ง แต่ละ span มี field สำคัญไม่กี่ตัว
- name — ประเภทของ operation เช่น
web.requestหรือpostgres.query - resource — สิ่งที่ทำแบบเจาะจงกว่านั้น เช่น
GET /checkoutหรือSELECT * FROM orders - start กับ duration — span เริ่มเมื่อไหร่และใช้เวลานานแค่ไหน
- ความสัมพันธ์ parent/child —
parent_idที่ชี้ไปที่ span ที่เป็นต้นเหตุให้ span นี้เกิดขึ้น
span ที่ไม่มี parent_id คือ root span — เป็นจุดเริ่มต้นของ trace มักเป็น inbound request ที่ทำให้ทุกอย่างเริ่มทำงาน span ที่เหลือทุกตัวใน trace ไล่สายกลับไปหา root นี้ทีละ parent นั่นคือสิ่งที่ทำให้ span กองหนึ่งกลายเป็น tree
{ "trace_id": "6141ee2f9a4d4b2e8f3c1a90", "span_id": "a1b2c3d4", "parent_id": null, "name": "web.request", "resource": "GET /checkout", "service": "checkout-web", "start": 1699999999000000000, "duration": 45000000}parent_id: null ตรงนี้เองที่ทำให้ span นี้เป็น root — span ทุกตัวที่อยู่ downstream ของตัวเอง (database query ที่ span นี้ trigger, การเรียก payments service และอื่น ๆ) จะมี parent_id ที่ไล่กลับมาที่นี่ในที่สุด
Auto-instrumentation เทียบกับ manual span
หัวข้อที่มีชื่อว่า “Auto-instrumentation เทียบกับ manual span”span ส่วนใหญ่ใน trace โผล่ขึ้นมาเองโดยไม่ต้องเขียน tracing code เลย Datadog tracing library เข้าไป hook กับ framework, HTTP client และ database driver ของคุณ — พอ request เข้ามา, พอมี outbound call ออกไป, พอ query รันขึ้น library จะสร้าง span ให้อัตโนมัติ นี่คือ auto-instrumentation และเป็นเหตุผลที่การติดตั้ง tracer เข้ากับ web app ทั่วไปให้ trace ที่ค่อนข้างสมบูรณ์ตั้งแต่วันแรก
แต่ auto-instrumentation หยุดที่ขอบของ business logic ของคุณเอง ถ้าอยากได้ visibility ของโค้ดบล็อกใดบล็อกหนึ่งโดยเฉพาะ — การคำนวณส่วนลด, step ใน batch job, cache lookup ที่เขียนเอง — ต้องห่อโค้ดส่วนนั้นด้วย manual span
from ddtrace import tracer
def process_order(order): # auto-instrumentation already created a span for the inbound HTTP request; # this manual span adds visibility into a specific block of business logic with tracer.trace("checkout.apply_discount", resource="apply_discount") as span: span.set_tag("order.id", order.id) discount = calculate_discount(order) span.set_tag("discount.amount", discount) return discountmanual span ตรงนี้กลายเป็น child ของ span ที่ active อยู่ตอนที่ process_order รัน มักเป็น root span web.request หรือ manual span อีกตัวที่ซ้อนอยู่ข้างบน auto-instrumentation กับ manual span ไม่ใช่เทคนิคที่แข่งกัน แต่รวมกันเป็น tree เดียวกัน
Service Map: สร้างจาก tree ไม่ใช่วาดเอง
หัวข้อที่มีชื่อว่า “Service Map: สร้างจาก tree ไม่ใช่วาดเอง”span ทุกตัวมี tag service ติดอยู่ — checkout-web, payments-service, inventory-service เมื่อ child ของ span หนึ่งมี tag service ต่างจาก parent นั่นคือ dependency จริงที่สังเกตเห็นระหว่างสอง service ณ ตอนที่ request ข้าม boundary นั้นจริง ๆ
Service Map คือ Datadog อ่าน signal ตัวนี้แหละจากทุก trace ที่ไหลผ่านระบบของคุณ แล้วรวม edge ทั้งหมดของ parent→child, service→service เข้าเป็นกราฟเดียวที่ live อยู่ตลอดเวลา เพราะประกอบขึ้นจากความสัมพันธ์ span จริง ไม่ใช่คนคอยดูแล Service Map จึง update ตัวเองทันทีที่ pattern ของ traffic เปลี่ยน call ใหม่ไปหา downstream โผล่บนแผนที่ทันทีที่เริ่มเกิดขึ้นจริง และ service ที่เลิกใช้แล้วก็หายไปจากแผนที่เมื่อไม่มีใครเรียกอีก เทียบกับไดอะแกรม architecture ที่วาดมือ ซึ่งถูกต้องแค่วันที่วาดแล้วก็เก่าไปในอีกเดือนถัดมา
flowchart LR
A[Root span: web.request service checkout-web] --> B[Span: db.query service checkout-web]
A --> C[Span: http.request service payments-service]
C --> D[Span: db.query service payments-service]
A --> E[Span: http.request service inventory-service]
subgraph SM[Service Map derived from the span tree]
S1[checkout-web] --> S2[payments-service]
S1 --> S3[inventory-service]
end
A -.observed service boundary crossings become.-> SM