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

Traces, Spans, and the Service Map

trace คือ journey ทั้งหมดของ request หนึ่งตัวตั้งแต่ต้นจนจบ ประกอบขึ้นจาก span ที่เรียงตัวเป็น tree แบบ parent/child และ Datadog สร้าง Service Map ขึ้นมาโดยอ่าน structure ของ tree นั้นตรง ๆ จาก traffic จริง ไม่ใช่จากไดอะแกรมที่ใครวาดไว้เอง

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/childparent_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 ที่ไล่กลับมาที่นี่ในที่สุด

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 discount

manual span ตรงนี้กลายเป็น child ของ span ที่ active อยู่ตอนที่ process_order รัน มักเป็น root span web.request หรือ manual span อีกตัวที่ซ้อนอยู่ข้างบน auto-instrumentation กับ manual span ไม่ใช่เทคนิคที่แข่งกัน แต่รวมกันเป็น 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
From span tree to Service Map
อะไรที่บอกว่า span หนึ่งเป็น root span ของ trace
ความต่างหลักระหว่าง auto-instrumentation กับ manual span คืออะไร
Service Map ถูกสร้างขึ้นจากอะไรจริง ๆ
ทำไม Service Map ถึงแม่นยำตลอดเวลาโดยไม่มีใครต้องมาคอย update เอง