10 Nov 2025·6 menit membaca

Tracing OpenTelemetry di Go untuk visibilitas end-to-end API

Penjelasan tracing OpenTelemetry di Go dengan langkah praktis untuk mengkorelasikan trace, metrik, dan log pada permintaan HTTP, pekerjaan latar belakang, dan panggilan pihak ketiga.

Tracing OpenTelemetry di Go untuk visibilitas end-to-end API

Apa arti end-to-end tracing untuk API Go

Trace adalah garis waktu dari satu permintaan saat bergerak melalui sistem Anda. Ia dimulai ketika panggilan API datang dan berakhir ketika Anda mengirim respons.

Di dalam trace ada span. Span adalah satu langkah yang diukur waktunya, seperti “parse request”, “jalankan SQL”, atau “panggil penyedia pembayaran”. Span juga bisa membawa detail berguna, seperti kode status HTTP yang aman, identifier pengguna yang disamarkan, atau berapa banyak baris yang dikembalikan query.

“End-to-end” berarti trace tidak berhenti di handler pertama Anda. Trace mengikuti permintaan melalui tempat-tempat di mana masalah biasanya bersembunyi: middleware, query database, panggilan cache, pekerjaan latar belakang, API pihak ketiga (pembayaran, email, peta), dan layanan internal lain.

Tracing paling bernilai saat masalah bersifat intermiten. Jika satu dari 200 permintaan lambat, log seringkali terlihat identik untuk kasus cepat dan lambat. Trace membuat perbedaan menjadi jelas: satu permintaan menghabiskan 800 ms menunggu panggilan eksternal, di-retry dua kali, lalu memicu pekerjaan lanjutan.

Log juga sulit dihubungkan antar layanan. Anda mungkin punya satu baris log di API, satu lagi di worker, dan tidak ada apa-apa di antaranya. Dengan tracing, peristiwa itu berbagi trace ID yang sama, sehingga Anda bisa mengikuti rantai tanpa menebak-nebak.

Trace, metrik, dan log: bagaimana mereka saling melengkapi

Trace, metrik, dan log menjawab pertanyaan yang berbeda.

Trace menunjukkan apa yang terjadi untuk satu permintaan nyata. Mereka memberi tahu Anda kemana waktu terpakai di handler, panggilan database, lookup cache, dan permintaan ke pihak ketiga.

Metrik menunjukkan tren. Mereka alat terbaik untuk alert karena stabil dan murah untuk diagregasi: persentil latensi, laju permintaan, tingkat error, kedalaman antrean, dan saturasi.

Log adalah “mengapa” dalam teks biasa: kegagalan validasi, input tak terduga, kasus tepi, dan keputusan yang dibuat kode Anda.

Kemenangan sebenarnya adalah korelasi. Ketika trace ID yang sama muncul di span dan log terstruktur, Anda bisa melompat dari log error ke trace yang tepat dan langsung melihat dependency mana yang memperlambat atau langkah mana yang gagal.

Model mental sederhana

Gunakan setiap sinyal untuk hal yang paling sesuai:

  • Metrik memberi tahu sesuatu salah.
  • Trace menunjukkan kemana waktu terpakai untuk satu permintaan.
  • Log menjelaskan keputusan kode Anda dan mengapa.

Contoh: endpoint POST /checkout mulai timeout. Metrik menunjukkan lonjakan p95. Trace menunjukkan sebagian besar waktu ada di panggilan ke penyedia pembayaran. Baris log yang terkorlasi di dalam span itu menunjukkan retry karena 502, yang mengarah ke pengaturan backoff atau insiden upstream.

Sebelum menambahkan kode: penamaan, sampling, dan apa yang harus dilacak

Sedikit perencanaan di depan membuat trace dapat dicari nanti. Tanpa itu, Anda tetap mengumpulkan data, tetapi pertanyaan dasar menjadi sulit: “Apakah ini staging atau prod?” “Layanan mana yang memulai masalah?”

Mulai dengan identitas yang konsisten. Pilih service.name yang jelas untuk setiap API Go (misalnya, checkout-api) dan satu field lingkungan seperti deployment.environment=dev|staging|prod. Jaga agar ini stabil. Jika nama berubah di tengah minggu, grafik dan pencarian terlihat seperti sistem berbeda.

Selanjutnya, tentukan sampling. Men-trace setiap permintaan bagus di pengembangan, tapi seringkali terlalu mahal di produksi. Pendekatan umum adalah men-sampling persentase kecil lalu lintas normal dan menyimpan trace untuk error dan permintaan lambat. Jika Anda sudah tahu endpoint tertentu ber-volume tinggi (health checks, polling), trace mereka lebih sedikit atau tidak sama sekali.

Terakhir, sepakati apa yang akan Anda tag di span dan apa yang tidak akan pernah dikumpulkan. Buat allowlist singkat atribut yang membantu menghubungkan peristiwa antar layanan, dan tulis aturan privasi sederhana.

Tag yang baik biasanya mencakup ID stabil dan info permintaan yang kasar (template route, method, status code). Hindari payload sensitif sepenuhnya: password, data pembayaran, email lengkap, token auth, dan badan permintaan mentah. Jika harus menyertakan nilai terkait pengguna, hash atau redaksi terlebih dahulu sebelum menambahkannya.

Langkah demi langkah: tambahkan tracing OpenTelemetry ke HTTP API Go

Anda akan men-setup tracer provider sekali saat startup. Ini menentukan ke mana span dikirim dan atribut resource mana yang dilampirkan ke setiap span.

1) Inisialisasi OpenTelemetry

Pastikan Anda mengatur service.name. Tanpanya, trace dari layanan berbeda tercampur dan grafik menjadi sulit dibaca.

// 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)

Itu adalah fondasi untuk tracing OpenTelemetry di Go. Selanjutnya, Anda perlu satu span per permintaan masuk.

2) Tambahkan middleware HTTP dan tangkap field kunci

Gunakan middleware HTTP yang memulai span otomatis dan merekam status code serta durasi. Set nama span menggunakan template route (seperti /users/:id), bukan URL mentah, atau Anda akan mendapat ribuan path unik.

Tujuannya baseline yang bersih: satu server span per permintaan, nama span berbasis route, status HTTP tercapture, kegagalan handler tercermin sebagai error pada span, dan durasi terlihat di viewer trace Anda.

3) Buat kegagalan jelas

Ketika sesuatu gagal, kembalikan error dan tandai span saat ini sebagai gagal. Itu membuat trace menonjol sebelum Anda melihat log.

Di handler, Anda bisa melakukan:

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

4) Verifikasi trace ID secara lokal

Jalankan API dan panggil endpoint. Log trace ID dari request context sekali untuk memastikan itu berubah per permintaan. Jika selalu kosong, middleware Anda tidak menggunakan context yang sama yang diterima handler.

Bawa context melalui DB dan panggilan pihak ketiga

Miliki kode sumber
Hasilkan kode siap-produksi yang bisa Anda ekspor dan instrumentasikan sesuai preferensi tim.
Coba AppMaster

Visibilitas end-to-end rusak saat Anda kehilangan context.Context. Context request masuk harus menjadi benang merah yang Anda teruskan ke setiap panggilan DB, panggilan HTTP, dan helper. Jika Anda menggantinya dengan context.Background() atau lupa meneruskannya, trace Anda berubah menjadi pekerjaan terpisah yang tidak terkait.

Untuk HTTP keluar, gunakan transport yang ter-instrumentasi sehingga setiap Do(req) menjadi child span di bawah request saat ini. Teruskan header trace W3C pada permintaan keluar agar layanan downstream dapat melampirkan span mereka ke trace yang sama.

Panggilan database perlu perlakuan yang sama. Gunakan driver ter-instrumentasi atau bungkus panggilan dengan span di sekitar QueryContext dan ExecContext. Rekam hanya detail yang aman. Anda ingin menemukan query lambat tanpa membocorkan data.

Atribut berguna dan rendah risiko termasuk nama operasi (misalnya, SELECT user_by_id), nama tabel atau model, jumlah baris (hanya hitung), durasi, jumlah retry, dan tipe error kasar (timeout, canceled, constraint).

Timeout adalah bagian dari cerita, bukan hanya kegagalan. Tetapkan dengan context.WithTimeout untuk DB dan panggilan pihak ketiga, dan biarkan pembatalan menggelembung ke atas. Saat panggilan dibatalkan, tandai span sebagai error dan tambahkan alasan singkat seperti deadline_exceeded.

Tracing pekerjaan latar belakang dan antrean

Pemetaan logika dengan alur visual
Ubah alur bisnis menjadi layanan yang bisa Anda telusuri melalui handler, job, dan dependency.
Bangun Sekarang

Pekerjaan latar belakang adalah tempat trace sering berhenti. Sebuah permintaan HTTP berakhir, lalu worker mengambil pesan nanti di mesin lain tanpa context yang dibagikan. Jika Anda tidak melakukan apa-apa, Anda mendapatkan dua cerita: trace API dan trace job yang tampak dimulai dari mana-mana.

Solusinya sederhana: ketika Anda enqueue job, tangkap context trace saat ini dan simpan di metadata job (payload, header, atau atribut, tergantung antrean Anda). Saat worker mulai, ekstrak context itu dan mulai span baru sebagai child dari permintaan asli.

Propagasi context dengan aman

Hanya salin context trace, bukan data pengguna.

  • Inject hanya identifier trace dan flag sampling (gaya W3C traceparent).
  • Pisahkan dari field bisnis (misalnya, field khusus "otel" atau "trace").
  • Perlakukan itu sebagai input yang tidak dipercaya saat membacanya kembali (validasi format, tangani data yang hilang).
  • Hindari menaruh token, email, atau body request ke metadata job.

Span yang dibuat (tanpa mengubah trace jadi berisik)

Trace yang mudah dibaca biasanya punya beberapa span bermakna, bukan puluhan kecil. Buat span di sekitar boundary dan titik "menunggu". Titik awal yang baik adalah span enqueue di handler API dan span job.run di worker.

Tambahkan sedikit konteks: nomor percobaan, nama antrean, tipe job, dan ukuran payload (bukan isi payload). Jika retry terjadi, rekam sebagai span atau event terpisah sehingga Anda bisa melihat delay backoff.

Tugas terjadwal juga butuh parent. Jika tidak ada permintaan masuk, buat root span baru untuk setiap run dan tag dengan nama jadwal.

Korelasikan log dengan trace (dan jaga log tetap aman)

Trace memberi tahu kemana waktu terpakai. Log menjelaskan apa yang terjadi dan mengapa. Cara paling sederhana menghubungkannya adalah menambahkan trace_id dan span_id ke setiap entri log sebagai field terstruktur.

Di Go, ambil span aktif dari context.Context dan perkaya logger Anda sekali per permintaan (atau job). Maka setiap baris log menunjuk ke trace spesifik.

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)

Itu cukup untuk melompat dari entri log ke span yang tepat yang berjalan saat itu. Ini juga membuat context yang hilang terlihat: trace_id akan kosong.

Buat log tetap berguna tanpa membocorkan PII

Log sering disimpan lebih lama dan bergerak lebih jauh daripada trace, jadi lebih ketat. Pilih identifier stabil dan hasil: user_id, order_id, payment_provider, status, dan error_code. Jika harus log input pengguna, redaksi dulu dan batasi panjangnya.

Buat error mudah dikelompokkan

Gunakan nama event dan tipe error yang konsisten sehingga Anda bisa menghitung dan mencari mereka. Jika kata-katanya berubah setiap kali, isu yang sama terlihat seperti banyak isu berbeda.

Tambahkan metrik yang benar-benar membantu menemukan masalah

Trace pekerjaan latar belakang
Buat worker dan propagasikan context sehingga job asinkron terhubung kembali ke request.
Mulai Sekarang

Metrik adalah sistem peringatan dini Anda. Dalam setup yang sudah menggunakan tracing OpenTelemetry di Go, metrik harus menjawab: seberapa sering, seberapa buruk, dan sejak kapan.

Mulai dengan set kecil yang bekerja untuk hampir semua API: jumlah permintaan, jumlah error (berdasarkan kelas status), persentil latensi (p50, p95, p99), permintaan in-flight, dan latensi dependency untuk DB dan panggilan pihak ketiga utama.

Agar metrik selaras dengan trace, gunakan template route dan nama yang sama. Jika span Anda menggunakan /users/{id}, metrik Anda juga harus demikian. Maka ketika grafik menunjukkan “p95 untuk /checkout melonjak,” Anda bisa langsung masuk ke trace yang difilter ke route itu.

Berhati-hatilah dengan label (atribut). Satu label buruk bisa meledakkan biaya dan membuat dashboard tak berguna. Template route, method, kelas status, dan nama layanan biasanya aman. User ID, email, URL lengkap, dan pesan error mentah biasanya tidak.

Tambahkan beberapa metrik kustom untuk event bisnis-krusial (misalnya, checkout started/completed, kegagalan pembayaran menurut grup kode hasil, job latar belakang sukses vs retry). Jaga set kecil dan hapus yang tidak pernah dipakai.

Mengekspor telemetri dan rollout aman

Ekspor adalah saat OpenTelemetry menjadi nyata. Layanan Anda harus mengirim span, metrik, dan log ke tempat yang andal tanpa memperlambat permintaan.

Untuk pengembangan lokal, buat sederhana. Exporter ke console (atau OTLP ke collector lokal) memungkinkan Anda melihat trace cepat dan memvalidasi nama span dan atribut. Di produksi, prefer OTLP ke agent atau OpenTelemetry Collector dekat layanan. Ini memberi satu tempat untuk menangani retry, routing, dan filtering.

Batching penting. Kirim telemetri dalam batch pada interval singkat, dengan timeout ketat sehingga jaringan macet tidak memblokir aplikasi Anda. Telemetri tidak boleh berada di jalur kritis. Jika exporter tidak bisa mengejar, sebaiknya data dijatuhkan daripada menumpuk di memori.

Sampling menjaga biaya tetap dapat diprediksi. Mulai dengan head-based sampling (misalnya, 1–10% permintaan), lalu tambahkan aturan sederhana: selalu sample error, dan selalu sample permintaan lambat di atas ambang. Jika Anda punya job latar belakang ber-volume tinggi, sample mereka pada tingkat lebih rendah.

Rollout lakukan bertahap: dev dengan 100% sampling, staging dengan traffic realistis dan sampling lebih rendah, lalu produksi dengan sampling konservatif dan alert pada kegagalan exporter.

Kesalahan umum yang merusak visibilitas end-to-end

Hubungkan frontend ke API yang ter-trace
Kirimkan aplikasi web dan mobile dengan backend yang bisa Anda debug menggunakan trace ID di log.
Coba Sekarang

Visibilitas end-to-end paling sering gagal karena alasan sederhana: datanya ada, tapi tidak terhubung.

Masalah yang merusak distributed tracing di Go biasanya:

  • Menjatuhkan context antara lapisan. Handler membuat span, tapi panggilan DB, client HTTP, atau goroutine memakai context.Background() bukan context request.
  • Mengembalikan error tanpa menandai span. Jika Anda tidak merekam error dan set status span, trace terlihat “hijau” meskipun pengguna melihat 500.
  • Menginstrumentasi segalanya. Jika setiap helper menjadi span, trace berubah menjadi bising dan biaya naik.
  • Menambah atribut high-cardinality. URL penuh dengan ID, email, nilai SQL mentah, body request, atau string error mentah dapat membuat jutaan nilai unik.
  • Menilai performa berdasarkan rata-rata. Insiden muncul di persentil (p95/p99) dan tingkat error, bukan rata-rata latency.

Pemeriksaan cepat: pilih satu permintaan nyata dan ikuti melintasi boundary. Jika Anda tidak bisa melihat satu trace ID mengalir melalui request inbound, query DB, panggilan pihak ketiga, dan worker asinkron, Anda belum punya visibilitas end-to-end.

Daftar centang praktis "selesai"

Deploy ke lingkungan pilihan
Deploy ke cloud Anda atau AppMaster Cloud tanpa mengubah cara Anda menginstrumentasi layanan.
Deploy Sekarang

Anda hampir selesai ketika bisa dari laporan pengguna ke permintaan tepat, lalu mengikuti lintasannya di setiap hop.

  • Pilih satu baris log API dan temukan trace tepatnya melalui trace_id. Konfirmasi log yang lebih dalam dari permintaan yang sama (DB, client HTTP, worker) membawa context trace yang sama.
  • Buka trace dan verifikasi nesting: server span HTTP di atas, dengan child span untuk panggilan DB dan API pihak ketiga. Daftar datar sering berarti context hilang.
  • Trigger job latar belakang dari permintaan API (misalnya, mengirimkan email struk) dan konfirmasi span worker terhubung kembali ke permintaan.
  • Periksa metrik dasar: jumlah permintaan, tingkat error, dan persentil latensi. Konfirmasi Anda bisa memfilter berdasarkan route atau operasi.
  • Pindai atribut dan log untuk keamanan: tidak ada password, token, nomor kartu kredit lengkap, atau data pribadi mentah.

Tes realitas sederhana adalah mensimulasikan checkout yang lambat saat penyedia pembayaran tertunda. Anda seharusnya melihat satu trace dengan span panggilan eksternal yang diberi label jelas, plus lonjakan metrik p95 untuk route checkout.

Jika Anda menghasilkan backend Go (misalnya, dengan AppMaster), membantu menjadikan daftar centang ini bagian dari rutinitas rilis sehingga endpoint dan worker baru tetap dapat ditelusuri saat aplikasi berkembang. AppMaster (appmaster.io) menghasilkan layanan Go nyata, jadi Anda bisa menstandarkan satu setup OpenTelemetry dan membawanya ke layanan dan pekerjaan latar belakang.

Contoh: debugging checkout lambat lintas layanan

Pesan pelanggan: “Checkout kadang macet.” Anda tidak bisa mereproduksinya kapan saja, dan di sinilah tracing OpenTelemetry di Go sangat berguna.

Mulailah dengan metrik untuk memahami bentuk masalah. Lihat laju permintaan, tingkat error, dan p95 atau p99 latency untuk endpoint checkout. Jika perlambatan terjadi dalam ledakan pendek dan hanya untuk sebagian permintaan, biasanya itu menunjuk ke dependency, antrean, atau perilaku retry daripada CPU.

Selanjutnya, buka trace lambat dari jendela waktu yang sama. Satu trace sering cukup. Checkout sehat mungkin 300–600 ms end-to-end. Yang buruk mungkin 8–12 detik, dengan sebagian besar waktu di dalam satu span.

Pola umum terlihat seperti ini: handler API cepat, kerja DB sebagian besar oke, lalu span penyedia pembayaran menunjukkan retry dengan backoff, dan panggilan downstream menunggu di belakang lock atau antrean. Respons mungkin tetap mengembalikan 200, sehingga alert berdasarkan error saja tidak pernah menyala.

Log yang terkorlasi kemudian memberi tahu jalur tepat dalam bahasa biasa: “retrying Stripe charge: timeout,” diikuti “db tx aborted: serialization failure,” lalu “retry checkout flow.” Itu sinyal jelas bahwa beberapa masalah kecil bergabung jadi pengalaman pengguna yang buruk.

Setelah menemukan bottleneck, konsistensi menjaga keterbacaan dari waktu ke waktu. Standarkan nama span, atribut (hash user ID yang aman, order ID, nama dependency), dan aturan sampling di seluruh layanan agar semua orang membaca trace dengan cara yang sama.

Mudah untuk memulai
Ciptakan sesuatu yang menakjubkan

Eksperimen dengan AppMaster dengan paket gratis.
Saat Anda siap, Anda dapat memilih langganan yang tepat.

Memulai