10 พ.ย. 2568·อ่าน 3 นาที

Go OpenTelemetry: การติดตามเพื่อมองเห็น API ตั้งแต่ต้นจนจบ

อธิบายการติดตามด้วย Go และ OpenTelemetry พร้อมขั้นตอนปฏิบัติ เพื่อเชื่อม trace, metrics และ logs ข้าม HTTP requests, งาน background และการเรียกภายนอก

Go OpenTelemetry: การติดตามเพื่อมองเห็น API ตั้งแต่ต้นจนจบ

ความหมายของการติดตามแบบ end-to-end สำหรับ API ที่เขียนด้วย Go

Trace คือไทม์ไลน์ของคำขอหนึ่งคำขอขณะที่มันเคลื่อนผ่านระบบของคุณ มันเริ่มเมื่อมีการเรียก API เข้ามาและจบเมื่อคุณส่งการตอบกลับกลับไป

ภายใน trace มี spans ซึ่งเป็นก้าวเวลาก้าวหนึ่ง เช่น “parse request”, “run SQL” หรือ “call payment provider” โดย span ยังสามารถเก็บรายละเอียดที่มีประโยชน์ เช่น รหัสสถานะ HTTP, ตัวระบุผู้ใช้ที่ไม่ละเอียดจนเกินไป, หรือจำนวนแถวที่คิวรีคืนมา

“End-to-end” หมายถึง trace ไม่หยุดที่ handler แรกของคุณ มันติดตามคำขอผ่านจุดที่ปัญหามักซ่อนอยู่: middleware, คิวรีฐานข้อมูล, การเรียกแคช, งาน background, API ภายนอก (เช่น การชำระเงิน, อีเมล, แผนที่) และบริการภายในอื่น ๆ

การติดตามมีค่าสูงสุดเมื่อปัญหาเป็นแบบไม่สม่ำเสมอ ถ้าหนึ่งใน 200 คำขอช้ากว่า ข้อมูลล็อกมักดูเหมือนกันทั้งในกรณีเร็วและช้า แต่ trace จะบอกชัดเจน: คำขอหนึ่งใช้เวลา 800 ms รอการเรียกภายนอก รีทรายสองครั้ง แล้วเริ่มงานติดตาม

ล็อกยังยากที่จะเชื่อมข้ามบริการ คุณอาจมีบรรทัดล็อกหนึ่งใน API อีกบรรทัดใน worker และไม่มีอะไรระหว่างนั้น ด้วย tracing เหตุการณ์เหล่านั้นจะแชร์ trace ID เดียวกัน ดังนั้นคุณจึงตามเส้นทางได้โดยไม่ต้องเดา

Traces, metrics และ logs: ทำงานร่วมกันอย่างไร

Traces, metrics และ logs ตอบคำถามต่างกัน

Traces แสดงว่าเกิดอะไรขึ้นสำหรับคำขอจริงหนึ่งคำขอ พวกมันบอกคุณว่าเวลาไปอยู่ที่ไหนบ้างระหว่าง handler, การเรียก DB, การค้นหาในแคช และการเรียกภายนอก

Metrics แสดงแนวโน้ม พวกมันเป็นเครื่องมือที่ดีที่สุดสำหรับการแจ้งเตือนเพราะรวมตัวได้เสถียรและถูก: พิสัยความหน่วง, อัตราคำขอ, อัตราข้อผิดพลาด, ความลึกของคิว, และ saturation

Logs คือคำตอบในรูปแบบข้อความธรรมดา: ข้อผิดพลาดการตรวจสอบความถูกต้อง, อินพุตที่ไม่คาดคิด, กรณีขอบ และการตัดสินใจที่โค้ดของคุณทำ

ชัยชนะที่แท้จริงคือการเชื่อมโยง เมื่อ trace ID เดียวกันปรากฏใน spans และล็อกเชิงโครงสร้าง คุณสามารถกระโดดจากล็อกข้อผิดพลาดไปยัง trace ที่แน่นอนและเห็นทันทีว่าพื้นที่ใดของ dependency ชะลอหรือขั้นตอนไหนล้มเหลว

แบบจำลองทางความคิดง่าย ๆ

ใช้แต่ละสัญญาณในสิ่งที่มันทำได้ดีที่สุด:

  • Metrics บอกคุณว่ามีบางอย่างผิดพลาด
  • Traces แสดงว่าเวลาไปอยู่ที่ไหนสำหรับคำขอหนึ่งคำขอ
  • Logs อธิบายว่าระบบตัดสินใจอะไรและทำไม

ตัวอย่าง: endpoint POST /checkout เริ่มเกิด timeout Metrics แสดง p95 latency พุ่งขึ้น Trace แสดงเวลาส่วนใหญ่ไปอยู่ในการเรียกผู้ให้บริการชำระเงิน ล็อกที่เกี่ยวข้องภายใน span นั้นแสดงการ retry เนื่องจาก 502 ซึ่งชี้ไปยังการตั้งค่า backoff หรือเหตุการณ์ด้านผู้ให้บริการภายนอก

ก่อนเพิ่มโค้ด: การตั้งชื่อ, การ sampling และสิ่งที่ควรติดตาม

การวางแผนเล็กน้อยล่วงหน้าทำให้ trace ค้นหาได้ทีหลัง ถ้าไม่ทำ คุณจะยังเก็บข้อมูลได้ แต่คำถามพื้นฐานจะยาก เช่น “นี่คือ staging หรือ prod?” “บริการไหนเริ่มปัญหา?”

เริ่มจากอัตลักษณ์ที่สม่ำเสมอ เลือก service.name ที่ชัดเจนสำหรับแต่ละ API ของ Go (เช่น checkout-api) และฟิลด์ environment เดียว เช่น deployment.environment=dev|staging|prod เก็บค่าเหล่านี้ให้คงที่ หากชื่อเปลี่ยนกลางสัปดาห์ ชาร์ตและการค้นหาจะดูเหมือนระบบต่างกัน

ต่อมา ตัดสินใจเรื่อง sampling การติดตามทุกคำขอดีใน development แต่ใน production มักแพงเกินไป วิธีที่ใช้กันคือ sample เพียงสัดส่วนเล็ก ๆ ของทราฟฟิกปกติ และเก็บ traces สำหรับข้อผิดพลาดและคำขอช้า หากคุณรู้แล้วว่า endpoint บางอันมีปริมาณสูง (เช่น health checks, polling) ให้ลดหรือไม่ติดตามพวกมัน

สุดท้าย ตกลงกันว่าคุณจะใส่แท็กอะไรใน spans และจะไม่เก็บอะไร เก็บ allowlist สั้น ๆ ของ attribute ที่ช่วยเชื่อมเหตุการณ์ข้ามบริการ และเขียนกฎความเป็นส่วนตัวอย่างง่าย

แท็กที่ดีมักรวมถึง ID ที่เสถียรและข้อมูลคำขอแบบหยาบ (เช่น template ของ route, method, status code) หลีกเลี่ยง payload ที่ละเอียดอ่อนทั้งหมด: รหัสผ่าน, ข้อมูลการชำระเงิน, อีเมลเต็ม, โทเค็น auth และเนื้อหา body แบบดิบ ถ้าต้องใส่ค่าที่เกี่ยวกับผู้ใช้ ให้แฮชหรือ redact ก่อนเพิ่ม

ขั้นตอนทีละข้อ: เพิ่ม OpenTelemetry tracing ให้กับ HTTP API ของ Go

คุณจะตั้ง tracer provider ครั้งเดียวตอนเริ่มต้น นี่คือจุดที่กำหนดว่าสแปนจะไปที่ไหนและ resource attributes ไหนจะถูกแนบกับทุก span

1) ติดตั้ง OpenTelemetry

ต้องแน่ใจว่าคุณตั้งค่า service.name หากไม่ตั้ง คำขอจากบริการต่าง ๆ จะปนกันและทำให้ชาร์ตอ่านยาก

// main.go (startup)
exp, _ := stdouttrace.New(stdouttrace.WithPrettyPrint())

res, _ := resource.New(context.Background(),
	resource.WithAttributes(
		semconv.ServiceName("checkout-api"),
	),
)

tp := sdktrace.NewTracerProvider(
	sdktrace.WithBatcher(exp),
	sdktrace.WithResource(res),
)

otel.SetTracerProvider(tp)

นั่นคือรากฐานของการติดตามด้วย Go OpenTelemetry ต่อไปคุณต้องสร้าง span สำหรับคำขอขาเข้าแต่ละคำขอ

2) เพิ่ม HTTP middleware และจับฟิลด์สำคัญ

ใช้ HTTP middleware ที่เริ่ม span อัตโนมัติและบันทึกรหัสสถานะและระยะเวลา ตั้งชื่อ span โดยใช้ template ของ route (เช่น /users/:id) ไม่ใช่ URL ดิบ มิฉะนั้นคุณจะได้เส้นทางเฉพาะเป็นหมื่น ๆ

ตั้งเป้าหมาย baseline ที่สะอาด: server span หนึ่งต่อคำขอ, ชื่อ span ตาม route, บันทึกรหัสสถานะ HTTP, ความล้มเหลวของ handler แสดงเป็นข้อผิดพลาดของ span, และระยะเวลามองเห็นได้ในตัวดู trace ของคุณ

3) ทำให้ความล้มเหลวเด่นชัด

เมื่อมีบางอย่างผิดพลาด ให้ return error และมาร์ก span ปัจจุบันว่า failed วิธีนี้ทำให้ trace โดดเด่นก่อนที่คุณจะดูล็อกด้วยซ้ำ

ใน handler คุณสามารถทำได้:

span := trace.SpanFromContext(r.Context())
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())

4) ตรวจสอบ trace ID ในเครื่อง

รัน API แล้วเรียก endpoint ใด endpoint หนึ่ง บันทึก trace ID จาก context ของคำขอหนึ่งครั้งเพื่อยืนยันว่ามันเปลี่ยนตามคำขอ หากมันว่างเสมอ แสดงว่า middleware ของคุณไม่ได้ใช้ context เดียวกับที่ handler ได้รับ

นำ context ไปกับการเรียก DB และการเรียกภายนอก

ปรับใช้ที่ต้องการ
ปรับใช้บนคลาวด์ของคุณหรือ AppMaster Cloud โดยไม่ต้องเปลี่ยนวิธีการติดตามบริการ
ปรับใช้ตอนนี้

ความสามารถในการเห็นแบบ end-to-end พังเมื่อคุณทิ้ง context.Context ควรใช้ context ที่มาจากคำขอขาเข้าเป็นเส้นใยที่คุณส่งต่อไปยังทุกการเรียก DB, การเรียก HTTP และ helper หากคุณแทนที่ด้วย context.Background() หรือลืมส่งต่อ คุณจะได้งานที่แยกเป็นคนละเรื่อง

สำหรับ HTTP ขาออก ให้ใช้ transport ที่ instrumented เพื่อให้ Do(req) ทุกตัวกลายเป็น child span ภายใต้คำขอปัจจุบัน ส่งต่อ header ตามมาตรฐาน W3C trace headers บนคำขอขาออกเพื่อให้บริการ downstream สามารถแนบ spans ของพวกมันกับ trace เดียวกันได้

การเรียกฐานข้อมูลต้องได้รับการปฏิบัติแบบเดียวกัน ใช้ไดรเวอร์ที่ instrumented หรือห่อการเรียกด้วย spans รอบ QueryContext และ ExecContext บันทึกเฉพาะรายละเอียดที่ปลอดภัย คุณต้องการหาคิวรีช้าที่สุดโดยไม่รั่วไหลข้อมูล

attribute ที่เป็นประโยชน์และความเสี่ยงต่ำได้แก่ ชื่อ operation (เช่น SELECT user_by_id), ชื่อ table หรือ model, จำนวนแถว (นับเท่านั้น), ระยะเวลา, จำนวน retry, และประเภทข้อผิดพลาดแบบหยาบ (timeout, canceled, constraint)

Timeout เป็นส่วนหนึ่งของเรื่อง ไม่ใช่แค่ความล้มเหลว ตั้งค่าด้วย context.WithTimeout สำหรับ DB และการเรียกภายนอก และให้การยกเลิกไหลขึ้น เมื่อการเรียกถูกยกเลิก ให้มาร์ก span เป็นข้อผิดพลาดและเพิ่มเหตุผลสั้น ๆ เช่น deadline_exceeded

ติดตามงาน background และคิว

ทำมาตรฐานการตั้งชื่อบริการ
สร้างบริการ Go จริงและรักษาชื่อบริการและแท็ก environment ให้สอดคล้องกันทั่วทั้งแอป
เริ่มต้น

งาน background มักเป็นจุดที่ trace หยุด คำขอ HTTP จบลง แล้ว worker หยิบข้อความขึ้นมาทีหลังบนเครื่องอื่นที่ไม่มี context ร่วมกัน หากคุณไม่ทำอะไร คุณจะได้สองเรื่อง: trace ของ API และ trace ของงานที่ดูเหมือนเริ่มจากที่ว่าง

วิธีแก้ตรงไปตรงมา: ตอน enqueue งาน ให้จับ context ของ trace ปัจจุบันและเก็บไว้ในเมตาดาต้าของงาน (payload, headers หรือ attributes ขึ้นกับคิว) เมื่อ worker เริ่ม ให้ extract context นั้นและเริ่ม span ใหม่เป็น child ของคำขอเดิม

กระจาย context อย่างปลอดภัย

คัดลอกเฉพาะ context ของ trace ไม่ใช่ข้อมูลผู้ใช้

  • Inject เฉพาะตัวระบุ trace และ flag ของการ sampling (แบบ W3C traceparent)
  • เก็บให้แยกจากฟิลด์ธุรกิจ (เช่น ฟิลด์ "otel" หรือ "trace")
  • ปฏิบัติต่อข้อมูลนั้นเป็น input ที่ไม่ไว้ใจเมื่ออ่านกลับ (validate รูปแบบ, จัดการข้อมูลที่หายไป)
  • หลีกเลี่ยงการใส่โทเค็น, อีเมล หรือ body ของคำขอลงในเมตาดาต้าของงาน

Spans ที่ควรเพิ่ม (โดยไม่ทำให้ trace เป็น noise)

trace ที่อ่านง่ายมักมีไม่กี่ span ที่มีความหมาย ไม่ใช่หลายสิบรายการเล็ก ๆ สร้าง span รอบ ๆ ขอบเขตและจุดที่รอ พื้นฐานที่ดีคือ span enqueue ใน API handler และ job.run ใน worker

เพิ่ม context เล็ก ๆ น้อย ๆ: หมายเลข attempt, ชื่อคิว, ประเภทงาน, และขนาด payload (แต่ไม่ใช่เนื้อหา payload) หากมีการ retry ให้บันทึกเป็น spans หรือ events แยกต่างหากเพื่อให้เห็นการหน่วงเวลาด้วย backoff

งานที่ทำตามตารางต้องมี parent ด้วย หากไม่มีคำขอขาเข้า ให้สร้าง root span ใหม่สำหรับแต่ละครั้งที่รันและแท็กด้วยชื่อ schedule

เชื่อมล็อกกับ trace (และเก็บล็อกให้ปลอดภัย)

Traces บอกว่าตรงไหนใช้เวลา Logs บอกว่าเกิดอะไรขึ้นและทำไม วิธีที่ง่ายที่สุดในการเชื่อมคือเพิ่ม trace_id และ span_id ให้กับทุกบรรทัดล็อกเป็นฟิลด์เชิงโครงสร้าง

ใน Go ดึง span ที่ active จาก context.Context และ enrich logger ของคุณครั้งหนึ่งต่อคำขอ (หรือ per job) แล้วทุกบรรทัดล็อกจะชี้ไปยัง trace ที่เฉพาะเจาะจง

span := trace.SpanFromContext(ctx)
sc := span.SpanContext()
logger := baseLogger.With(
  "trace_id", sc.TraceID().String(),
  "span_id",  sc.SpanID().String(),
)
logger.Info("charge_started", "order_id", orderID)

นั่นพอจะให้คุณกระโดดจากบันทึกล็อกไปยัง span ที่กำลังรันเมื่อเหตุการณ์นั้นเกิดขึ้น มันยังทำให้เห็นว่าขาด context ได้ง่าย: trace_id จะว่าง

ทำให้ล็อกมีประโยชน์โดยไม่รั่วไหล PII

ล็อกมักเก็บนานและส่งต่อไกลกว่า traces ดังนั้นต้องเข้มงวดกว่า เลือกใช้ตัวระบุและผลลัพธ์ที่เสถียร: user_id, order_id, payment_provider, status, และ error_code หากต้องล็อกอินพุตของผู้ใช้ ให้ redact ก่อนและจำกัดความยาว

ทำให้การรวมกลุ่มข้อผิดพลาดง่ายขึ้น

ใช้ชื่อตัวเหตุการณ์และประเภทข้อผิดพลาดที่สอดคล้องกันเพื่อให้คุณสามารถนับและค้นหาได้ หากข้อความเปลี่ยนไปทุกครั้ง เหตุการณ์เดียวกันจะดูเหมือนหลายเรื่อง

เพิ่ม metrics ที่ช่วยคุณหาปัญหาจริง ๆ

จัดการเทเลเมทรีให้ง่ายขึ้น
เพิ่ม attribute อย่างปลอดภัยและหลีกเลี่ยงแท็กที่มี cardinality สูงด้วยโครงสร้างแอปที่ชัดเจน
เริ่มสร้าง

Metrics เป็นระบบเตือนล่วงหน้าของคุณ ในการตั้งค่าที่ใช้ Go OpenTelemetry tracing อยู่แล้ว metrics ควรตอบคำถาม: บ่อยแค่ไหน แย่แค่ไหน และตั้งแต่เมื่อไหร่

เริ่มจากชุดเล็ก ๆ ที่เหมาะกับแทบทุก API: จำนวนคำขอ, จำนวนข้อผิดพลาด (ตาม class ของสถานะ), พิสัยความหน่วง (p50, p95, p99), in-flight requests, และ latency ของ dependencies สำคัญเช่น DB และการเรียกภายนอกหลัก

เพื่อให้ metrics สอดคล้องกับ traces ใช้ template ของ route และชื่อเดียวกัน หาก spans ของคุณใช้ /users/{id} metrics ควรใช้เหมือนกัน แล้วเมื่อชาร์ตบอกว่า "p95 สำหรับ /checkout พุ่ง" คุณจะกระโดดเข้าไปใน traces ที่กรองตาม route นั้นได้ทันที

ระวัง labels (attributes) หนึ่ง label ผิดอาจทำให้ต้นทุนพุ่งและทำให้แดชบอร์ดใช้ไม่ได้ รูปแบบที่ปลอดภัยได้แก่ route template, method, status class, และ service name ปกติ user IDs, อีเมล, URLs เต็มรูปแบบ และข้อความข้อผิดพลาดดิบไม่ควรใช้

เพิ่ม metrics เฉพาะกิจที่สำคัญทางธุรกิจ (เช่น checkout started/completed, payment failures ตามกลุ่มรหัสผลลัพธ์, งาน background สำเร็จ vs retry) เก็บชุดเล็ก ๆ และลบสิ่งที่ไม่ใช้

การส่งออกเทเลเมทรีและการเปิดใช้แบบค่อยเป็นค่อยไป

การส่งออกคือจุดที่ OpenTelemetry กลายเป็นของจริง บริการของคุณต้องส่ง spans, metrics และ logs ไปยังที่ที่เชื่อถือได้โดยไม่ทำให้คำขอช้าลง

สำหรับการพัฒนาในเครื่อง ให้เริ่มง่าย ๆ exporter แบบ console (หรือ OTLP ไปยัง collector ท้องถิ่น) เพื่อดู traces อย่างรวดเร็วและตรวจสอบชื่อ span กับ attributes ใน production ให้ใช้ OTLP ไปยัง agent หรือ OpenTelemetry Collector ใกล้กับบริการ มันให้จุดเดียวในการจัดการ retry, routing และ filtering

Batching สำคัญ ส่งเทเลเมทรีเป็นชุดในช่วงเวลาสั้น ๆ พร้อม timeout ที่เข้มงวดเพื่อไม่ให้เครือข่ายติดขัดบล็อกแอป เทเลเมทรีไม่ควรอยู่บนเส้นทางวิกฤต หาก exporter ไล่ไม่ทัน ควรทิ้งข้อมูลแทนการสะสมในหน่วยความจำ

Sampling ทำให้ต้นทุนคาดการณ์ได้ เริ่มจาก head-based sampling (เช่น 1–10% ของคำขอ) แล้วเพิ่มกฎง่าย ๆ: เก็บข้อผิดพลาดเสมอ และเก็บคำขอที่ช้ากว่า threshold เสมอ หากงาน background มีปริมาณสูง ให้ sample ที่อัตราต่ำกว่า

ค่อย ๆ เปิดใช้งาน: dev ด้วย 100% sampling, staging กับทราฟฟิกที่สมจริงและ sampling ต่ำลง, แล้ว production ด้วย sampling รักษาและมีการแจ้งเตือนเมื่อ exporter ล้มเหลว

ความผิดพลาดทั่วไปที่ทำให้การมองเห็นแบบ end-to-end ล้มเหลว

ออกแบบ backend แบบ DB-first
ออกแบบข้อมูลใน PostgreSQL แบบภาพ แล้วเพิ่มการติดตามรอบ ๆ คิวรีที่สำคัญ
สร้างแอป

การมองเห็นแบบ end-to-end มักพังเพราะเหตุผลง่าย ๆ: ข้อมูลมีอยู่ แต่เชื่อมต่อไม่ได้

ปัญหาที่ทำให้ distributed tracing ใน Go พังมักเป็น:

  • ทิ้ง context ข้ามเลเยอร์ handler สร้าง span แต่ DB call, HTTP client หรือ goroutine ใช้ context.Background() แทน context ของคำขอ
  • คืนข้อผิดพลาดโดยไม่มาร์ก span หากไม่ record error และตั้งสถานะ span ข้อผิดพลาด trace จะดู "เขียว" แม้ผู้ใช้จะเห็น 500
  • instrument ทุกอย่าง หาก helper ทุกตัวกลายเป็น span trace จะกลายเป็นเสียงรบกวนและค่าใช้จ่ายสูงขึ้น
  • เพิ่ม attribute ที่มี cardinality สูง URL แบบเต็มที่มี ID, อีเมล, ค่า SQL ดิบ, body ของคำขอ หรือข้อความข้อผิดพลาดแบบดิบ สามารถสร้างค่าที่เป็นเอกลักษณ์เป็นล้าน
  • ตัดสินประสิทธิภาพจากค่าเฉลี่ย เหตุการณ์จะปรากฏใน percentiles (p95/p99) และอัตราข้อผิดพลาด ไม่ใช่ mean latency

การตรวจสอบอย่างรวดเร็วคือเลือกคำขอจริงหนึ่งคำขอแล้วตามมันข้ามขอบเขต หากคุณไม่เห็น trace ID เดียวไหลจากคำขอขาเข้าไปยังการคิวรี DB การเรียกภายนอก และ worker แบบ async แปลว่ายังไม่มีการมองเห็นแบบ end-to-end

เช็คลิสต์ "เสร็จ" แบบปฏิบัติ

สร้าง Go API ที่ติดตามได้
สร้าง backend ด้วย Go ที่ติดตามได้แบบ end-to-end ขณะที่ API โตขึ้น
ลองใช้ AppMaster

คุณใกล้เสร็จเมื่อคุณสามารถไปจากรายงานผู้ใช้ถึงคำขอที่แน่นอน แล้วตามมันข้ามทุก hop ได้

  • เลือกบรรทัดล็อก API หนึ่งบรรทัดและหาตัว trace ที่ตรงด้วย trace_id ยืนยันว่าล็อกภายในคำขอเดียวกัน (DB, HTTP client, worker) มี context trace เดียวกัน
  • เปิด trace และตรวจสอบการซ้อน: server span ด้านบน และ child spans สำหรับการเรียก DB และ API ภายนอก หากเป็นรายการแบนมักหมายความว่า context หาย
  • ทริกเกอร์งาน background จากคำขอ API (เช่น ส่งใบเสร็จทางอีเมล) และยืนยันว่า worker span เชื่อมกลับไปยังคำขอ
  • ตรวจสอบ metrics เบื้องต้น: จำนวนคำขอ, อัตราข้อผิดพลาด, และพิสัยความหน่วง ยืนยันว่าคุณสามารถกรองตาม route หรือ operation ได้
  • สแกน attributes และล็อกเพื่อความปลอดภัย: ไม่มีรหัสผ่าน, โทเค็น, หมายเลขบัตรเครดิตเต็ม หรือข้อมูลส่วนบุคคลดิบ

การทดสอบเรียบง่ายคือจำลอง checkout ช้าที่ผู้ให้บริการชำระเงินหน่วง คุณควรเห็น trace เดียวที่มี external call span ติดป้ายชัดเจน และชาร์ต p95 latency ของ route checkout กระโดดขึ้น

ถ้าคุณสร้าง backend ด้วย Go (เช่น ด้วย AppMaster) ช่วยให้เอาเช็คลิสต์นี้เป็นส่วนหนึ่งของขั้นตอนปล่อยเพื่อให้ endpoint และ worker ใหม่ยังคงติดตามได้เมื่อแอปโตขึ้น AppMaster (appmaster.io) สร้างบริการ Go จริง ดังนั้นคุณสามารถทำมาตรฐานการตั้งค่า OpenTelemetry หนึ่งชุดและนำไปใช้ข้ามบริการและงาน background ได้

ตัวอย่าง: ดีบัก checkout ช้าที่เกิดข้ามบริการ

ข้อความจากลูกค้า: “Checkout ค้างเป็นบางครั้ง” คุณจับมันซ้ำไม่ได้ตามคำสั่ง ซึ่งเป็นสถานการณ์ที่ Go OpenTelemetry tracing ช่วยได้

เริ่มจาก metrics เพื่อเข้าใจรูปแบบของปัญหา ดูอัตราคำขอ อัตราข้อผิดพลาด และ p95 หรือ p99 latency ของ endpoint checkout หากความช้าปรากฏเป็นช่วงสั้น ๆ และเพียงบางคำขอ มักชี้ไปที่ dependency, queuing, หรือพฤติกรรม retry มากกว่าปัญหา CPU

ต่อมา เปิด slow trace จากหน้าต่างเวลานั้น หนึ่ง trace มักพอ Trace ปกติอาจใช้ 300–600 ms end-to-end แต่ตัวที่แย่อาจ 8–12 วินาที โดยเวลาส่วนใหญ่ไปอยู่ใน span เดียว

รูปแบบที่พบบ่อยคือ handler ของ API ทำงานเร็ว งาน DB ส่วนใหญ่โอเค แล้ว span ของ payment provider แสดงการ retry พร้อม backoff และการเรียกด้านล่างรออยู่ข้างหลังล็อกหรือคิว การตอบกลับอาจยังคืน 200 ดังนั้นการแจ้งเตือนจากข้อผิดพลาดอย่างเดียวจะไม่จับ

ล็อกที่เกี่ยวข้องจะบอกเส้นทางที่แน่นอนด้วยภาษาธรรมดา: “retrying Stripe charge: timeout” ตามด้วย “db tx aborted: serialization failure” ตามด้วย “retry checkout flow” นั่นคือสัญญาณชัดเจนว่าคุณเจอปัญหาจากหลายเรื่องเล็ก ๆ รวมกันเป็นประสบการณ์ที่แย่

เมื่อคุณพบคอขวด ความสม่ำเสมอคือสิ่งที่ทำให้ใช้งานได้ยาวนาน ทำมาตรฐานชื่อ span, attributes (เช่น hash ของ user ID ที่ปลอดภัย, order ID, ชื่อ dependency) และกฎการ sampling ข้ามบริการเพื่อให้ทุกคนอ่าน trace ในแบบเดียวกัน

ง่ายต่อการเริ่มต้น
สร้างบางสิ่งที่ น่าทึ่ง

ทดลองกับ AppMaster ด้วยแผนฟรี
เมื่อคุณพร้อม คุณสามารถเลือกการสมัครที่เหมาะสมได้

เริ่ม