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

เชื่อม Log เข้ากับ Trace

tracer รู้อยู่แล้วว่า span ไหน active อยู่ตอนโค้ดกำลังจัดการ request หนึ่ง ดังนั้น log injection ก็แค่ tracer ส่ง trace_id (กับ span_id) ของ span ที่ active นั้นให้ logging library ของเรา ทำให้ log ทุกบรรทัดที่เขียนระหว่าง request นั้นพก identifier ที่ใช้ตามหา trace ต้นทางกลับไปได้

2026-07-08T10:14:02Z ERROR Failed to charge card for order 48213

log บรรทัดนี้บอกแค่ว่ามีอะไรพัง — ไม่บอกว่า downstream call ไหนช้า service ไหนใน chain เป็นคนโยน error ออกมาจริง ๆ หรือมีอะไรอื่นเกิดขึ้นอีกใน request เดียวกัน ถ้าไม่มี identifier ที่ประทับอยู่ทั้งใน log และ trace การ correlate สองอย่างนี้เข้าด้วยกันจะกลายเป็นการเดาจาก timestamp แล้วหวังว่าจะไม่มีอย่างอื่นเกิดบน host เดียวกันในวินาทีเดียวกัน

tracer ทุกตัวเก็บ span ที่ active อยู่ไว้ใน context ที่ scope ตาม request — thread-local ถ้า runtime เป็นแบบ threaded, async-local (หรือ continuation-local) ถ้า runtime เป็นแบบ event-loop log injection คือ hook เล็ก ๆ ที่เสียบเข้ากับ logging library ของเรา — อาจเป็น formatter, processor หรือ logger method ที่ patch ไว้ — รันทุกครั้งที่มี log record แล้วถาม tracer ว่า “ตอนนี้ span ไหน active อยู่” ถ้ามี จะ copy trace_id กับ span_id ของ span นั้นแปะเข้าไปใน record ก่อนปล่อยออกไป เปิดใช้งานครั้งเดียว แล้ว log statement ทุกจุดที่มีอยู่แล้วในโค้ดเบสได้ประโยชน์ทันที โดยไม่ต้องแตะทีละบรรทัด

require('dd-trace').init({
log_injection: true
});

flag ตัวนี้คือขั้นตอน integration ทั้งหมดสำหรับ service ที่เขียนด้วย Node.js และใช้ dd-trace พอตั้งค่านี้แล้ว log ทุกอันที่ปล่อยผ่าน logging library ที่รองรับ (pino, winston, bunyan) จะได้ trace context ที่ active อยู่แปะเข้าไปใน structured output อัตโนมัติ — ไม่ต้องแก้ logger.info(...) หรือ logger.error(...) ทีละจุดในแอปเลย

{
"level": "error",
"message": "Failed to charge card for order 48213",
"dd": {
"trace_id": "6746734253672554013",
"span_id": "3993638273767594310",
"env": "production",
"service": "checkout-service",
"version": "2.4.1"
}
}

tracer ของ Datadog รวมฟิลด์ที่ inject เข้าไปเหล่านี้ไว้ใต้ namespace dd.* dd.trace_id กับ dd.span_id คือ key ที่ใช้ pivot — ชี้ไปที่ trace และ span ตัวที่กำลังรันอยู่ตอนบรรทัดนี้ถูกเขียน dd.env, dd.service และ dd.version คือ unified service tagging triple ที่ติดมาด้วย หมายความว่า log บรรทัดเดียวนี้พก identity มากพอที่จะถูกค้นเจอผ่าน structured logging กับ facet ที่สร้างไว้ใน module Log Management ได้ด้วย — ไม่ได้แค่ correlate กับ trace ได้ แต่ยังเป็น log event ที่ facet ได้เต็มรูปแบบในตัวเอง

ก่อนมี log injection log บรรทัดระดับ error หนึ่งบรรทัดอยู่โดดเดี่ยว ไม่มีทางง่าย ๆ ที่จะเห็น request รอบข้าง — อะไรเรียก service นี้ service นี้เรียกอะไรต่อ เวลาไปหมดตรงไหน หลังมี log injection เพราะ log พก dd.trace_id ไปด้วย Datadog ใส่ปุ่ม pivot “view trace” ลงบน log บรรทัดนั้นใน Logs Explorer ได้เลย คลิกเดียวแล้วไปถึง distributed trace ตัวเป๊ะ ๆ ที่มีทุก span จากทุก service ที่มีส่วนร่วมใน request นั้น ซึ่ง span พวกนั้นไม่เคยเห็น log เลยด้วยซ้ำ

flowchart LR
  A[Incoming request] --> B[Tracer starts span]
  B --> C["App code calls logger.error(...)"]
  C --> D["Log injection hook reads active span"]
  D --> E["Log emitted with dd.trace_id, dd.span_id, dd.env/service/version"]
  E --> F[Datadog Log Management]
  F -->|click dd.trace_id| G[APM distributed trace view]
From a log line to its trace
log injection integration ของ tracer ทำอะไรกับ log record จริง ๆ
ใน require('dd-trace').init({ log_injection: true }) ต้องทำอะไรเพิ่มในส่วนอื่นของแอปเพื่อให้ log ทุกบรรทัดได้ trace context ที่ inject เข้ามา
dd.env, dd.service และ dd.version ที่โผล่มาใน log บรรทัดที่ inject แล้ว คืออะไร
payoff จริง ๆ ของการเชื่อม log บรรทัดหนึ่งเข้ากับ trace ผ่าน dd.trace_id คืออะไร