เชื่อม Log เข้ากับ Trace
ไอเดียหลักในหนึ่งประโยค
หัวข้อที่มีชื่อว่า “ไอเดียหลักในหนึ่งประโยค”tracer รู้อยู่แล้วว่า span ไหน active อยู่ตอนโค้ดกำลังจัดการ request หนึ่ง ดังนั้น log injection ก็แค่ tracer ส่ง trace_id (กับ span_id) ของ span ที่ active นั้นให้ logging library ของเรา ทำให้ log ทุกบรรทัดที่เขียนระหว่าง request นั้นพก identifier ที่ใช้ตามหา trace ต้นทางกลับไปได้
ถ้าไม่มี: log บรรทัดหนึ่งที่ไปต่อไม่ได้
หัวข้อที่มีชื่อว่า “ถ้าไม่มี: log บรรทัดหนึ่งที่ไปต่อไม่ได้”2026-07-08T10:14:02Z ERROR Failed to charge card for order 48213log บรรทัดนี้บอกแค่ว่ามีอะไรพัง — ไม่บอกว่า downstream call ไหนช้า service ไหนใน chain เป็นคนโยน error ออกมาจริง ๆ หรือมีอะไรอื่นเกิดขึ้นอีกใน request เดียวกัน ถ้าไม่มี identifier ที่ประทับอยู่ทั้งใน log และ trace การ correlate สองอย่างนี้เข้าด้วยกันจะกลายเป็นการเดาจาก timestamp แล้วหวังว่าจะไม่มีอย่างอื่นเกิดบน host เดียวกันในวินาทีเดียวกัน
กลไกทั่วไป: log integration อ่าน active span
หัวข้อที่มีชื่อว่า “กลไกทั่วไป: log integration อ่าน active span”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 ทุกจุดที่มีอยู่แล้วในโค้ดเบสได้ประโยชน์ทันที โดยไม่ต้องแตะทีละบรรทัด
ตัวอย่างจริงหนึ่งภาษา: Node.js
หัวข้อที่มีชื่อว่า “ตัวอย่างจริงหนึ่งภาษา: Node.js”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(...) ทีละจุดในแอปเลย
convention dd.*: identity ที่ติดไปกับ log บรรทัดนั้น
หัวข้อที่มีชื่อว่า “convention dd.*: identity ที่ติดไปกับ log บรรทัดนั้น”{ "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 ได้เต็มรูปแบบในตัวเอง
payoff: คลิกเดียว ไม่ต้องค้นเอง
หัวข้อที่มีชื่อว่า “payoff: คลิกเดียว ไม่ต้องค้นเอง”ก่อนมี 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]