Skip to content

8.1 Tracing

Tujuan bab

Setelah bab ini kamu bisa:

  • Mengaktifkan observability dan menjelaskan apa yang direkam sebuah trace
  • Membaca satu trace untuk mendiagnosis agent yang berperilaku aneh
  • Mengurangi kebisingan dan biaya dengan sampling, excludeSpanTypes, dan spanFilter
  • Menjelaskan peran sensitiveDataFilter dan kenapa ia menyala secara default
  • Mengambil metadata dari request context ke dalam trace

Prasyarat

Apa yang direkam sebuah trace

Ingat dari 1.1 bahwa agent adalah loop, dan dari 2.3 bahwa satu pertanyaan dengan tool berarti beberapa panggilan model. Semua itu tidak terlihat dari luar — kamu hanya melihat pertanyaan masuk dan jawaban keluar.

Trace membuka kotak hitam itu. Ia merekam tiap operasi sebagai span, dan span-span itu tersusun secara hierarkis.

Trace: "Cuaca di Surabaya dan Jakarta?"           2.4 dtk
├── Agent run                                     2.4 dtk
│   ├── Model call #1                             0.6 dtk
│   │   └── keputusan: panggil get-weather ×2
│   ├── Tool: get-weather (Surabaya)              0.3 dtk
│   ├── Tool: get-weather (Jakarta)               1.1 dtk  ← lambat
│   └── Model call #2                             0.4 dtk
│       └── jawaban akhir

Dari satu gambar itu kamu langsung tahu ke mana waktu pergi, tool apa yang dipanggil dengan argumen apa, dan berapa kali model dipanggil. Pertanyaan yang sebelumnya butuh sepuluh console.log terjawab dalam sekali lihat.

Mengaktifkannya

Tracing dikonfigurasi lewat paket terpisah, @mastra/observability:

bash
npm install @mastra/observability@latest
ts
// src/mastra/index.ts
import { Mastra } from '@mastra/core/mastra'
import { LibSQLStore } from '@mastra/libsql'
import {
  Observability,
  MastraStorageExporter,
  SensitiveDataFilter,
} from '@mastra/observability'

export const mastra = new Mastra({
  storage: new LibSQLStore({ id: 'mastra-storage', url: 'file:./mastra.db' }),
  observability: new Observability({
    configs: {
      default: {
        serviceName: 'mastra',
        exporters: [new MastraStorageExporter()],
        spanOutputProcessors: [new SensitiveDataFilter()],
      },
    },
  }),
})

observability menerima instance, bukan objek konfigurasi

Ini titik yang mudah salah. Properti observability pada Mastra bertipe ObservabilityEntrypoint — sebuah objek yang sudah di-instantiasi, bukan objek konfigurasi biasa.

Bentuk seperti observability: { default: { enabled: true } } yang muncul di sebagian contoh akan ditolak compiler:

Object literal may only specify known properties,
and 'default' does not exist in type 'ObservabilityEntrypoint'

Yang benar: bungkus konfigurasimu dengan new Observability({ ... }) dari @mastra/observability. Objek konfigurasi yang kamu berikan ke dalam konstruktor itulah yang bentuknya ObservabilityRegistryConfig — dan di situlah default, configs, dan sensitiveDataFilter berada.

Untuk mulai cepat, bentuk paling ringkas cukup begini:

ts
observability: new Observability({
  default: { enabled: true },
}),

default: { enabled: true } menyalakan konfigurasi bawaan lengkap dengan exporter ke storage Mastra dan ke platform Mastra. Contoh eksplisit di atas setara dengan ini, tapi memperlihatkan bagian-bagiannya — dan itu yang kamu butuhkan begitu ingin menyesuaikan sesuatu.

Tiga bagian yang kamu susun di dalamnya:

BagianPerannya
serviceNameNama layanan yang muncul di trace
exportersKe mana data trace dikirim
spanOutputProcessorsPemroses span sebelum diekspor — termasuk penyensor data sensitif

Exporter menentukan tujuan. MastraStorageExporter menulis ke storage yang kamu konfigurasi; MastraPlatformExporter mengirim ke platform Mastra. Keduanya bisa dipakai bersamaan:

ts
exporters: [new MastraStorageExporter(), new MastraPlatformExporter()],

Trace-nya bisa langsung kamu lihat di Studio — dan inilah kenapa kebiasaan membuka Studio yang dianjurkan sejak 1.1 mulai terbayar.

Konfigurasi yang lebih terkendali

Nama konfigurasi tidak harus default. Kamu bisa memberi nama sesuai lingkungan dan menyetel perilakunya lebih rinci:

ts
// src/mastra/index.ts
import { Mastra } from '@mastra/core/mastra'
import { LibSQLStore } from '@mastra/libsql'
import { Observability, MastraStorageExporter } from '@mastra/observability'

export const mastra = new Mastra({
  storage: new LibSQLStore({ id: 'mastra-storage', url: 'file:./mastra.db' }),
  observability: new Observability({
    configs: {
      produksi: {
        serviceName: 'layanan-support',
        exporters: [new MastraStorageExporter()],
        includeInternalSpans: false,
        requestContextKeys: ['tenantId', 'userId'],
      },
    },
  }),
})

Properti yang paling berpengaruh:

PropertiKegunaan
serviceNameNama layanan yang muncul di trace
samplingStrategi pengambilan sampel — default merekam semuanya
exportersKe mana data trace dikirim
includeInternalSpansMenyertakan span internal operasi Mastra
excludeSpanTypesJenis span yang tidak diekspor
spanFilterFungsi penyaring: kembalikan true untuk menyimpan
requestContextKeysKunci RequestContext yang diambil sebagai metadata

requestContextKeys — menghubungkan trace ke penggunanya

Ini yang membuat trace berguna di produksi. Tanpa metadata, kamu punya ribuan trace tanpa cara memfilternya. Dengan requestContextKeys, nilai dari RequestContexttenantId, userId, plan — ikut terekam sebagai metadata trace.

Sekarang saat seorang pelanggan melapor "asisten memberi jawaban salah tadi pagi", kamu bisa mencari trace miliknya, bukan menebak.

Perhatikan bahwa properti ini mendukung notasi titik, sehingga nilai bersarang juga bisa diambil.

Mengurangi kebisingan dan biaya

Merekam semuanya bagus saat pengembangan dan mahal di produksi — terutama pada platform yang menagih per span.

Tiga tuas tersedia, dan urutan penerapannya penting:

excludeSpanTypes → spanOutputProcessors → spanFilter

Dokumentasi menyebut bahwa spanFilter berjalan setelah excludeSpanTypes dan spanOutputProcessors.

sampling — default-nya ALWAYS, artinya semua direkam. Turunkan untuk lalu lintas tinggi.

excludeSpanTypes — buang jenis span tertentu sama sekali. Berguna untuk mengurangi kebisingan pada platform dengan penagihan per span.

spanFilter — fungsi yang mengembalikan true untuk menyimpan dan false untuk membuang. Ini yang paling luwes: kamu bisa, misalnya, merekam semua trace yang gagal dan hanya sebagian kecil yang berhasil.

Pola yang berguna di produksi

Simpan semua trace yang mengandung error, dan sebagian kecil yang sukses. Trace yang sukses sebagian besar seragam; trace yang gagal justru selalu berbeda, dan itu yang kamu butuhkan saat mendiagnosis.

Data sensitif

Ini perilaku default yang penting diketahui, karena melindungimu dari kesalahan yang mudah terjadi.

sensitiveDataFilter menyala secara default. Ia otomatis diterapkan ke tiap instance observability yang dikonfigurasi, menyensor field yang dianggap sensitif.

Letaknya di konstruktor Observability, sejajar dengan configs:

ts
observability: new Observability({
  configs: { /* ... */ },
  sensitiveDataFilter: false, // ← mematikannya, jangan lakukan ini
}),

Kamu bisa mematikannya dengan false, atau menyesuaikannya dengan objek opsi untuk mengatur field mana yang disensor, token penggantinya, dan gaya penyensorannya.

Pikirkan dua kali sebelum mematikannya

Trace merekam argumen tool dan isi pesan. Tanpa penyaring, itu berarti nomor kartu, token, dan data pribadi pengguna ikut tersimpan di sistem observability-mu — sering kali dengan retensi panjang dan akses yang lebih longgar daripada basis data utamamu.

Kalau kamu perlu melihat nilai yang tersensor untuk diagnosis, sesuaikan daftar field-nya alih-alih mematikan penyaringnya.

Dua catatan tambahan dari dokumentasi: kalau sebuah konfigurasi sudah memuat SensitiveDataFilter di spanOutputProcessors, ia tidak diduplikasi. Dan instance yang sudah di-instantiasi sebelumnya tidak dimodifikasi.

Membaca trace untuk diagnosis

Tiga pola yang paling sering kamu cari:

Agent lambat. Lihat durasi tiap span. Biasanya penyebabnya salah satu dari dua: satu tool yang lambat, atau terlalu banyak putaran loop. Yang pertama masalah integrasi; yang kedua biasanya masalah instruksi yang ambigu.

Agent memanggil tool yang salah. Lihat argumen pada span tool. Sering kali argumennya sendiri sudah aneh, yang menunjuk ke description atau schema yang kurang jelas — kembali ke 2.2.

Agent mengabaikan konteks RAG. Lihat apakah span tool pencarian benar-benar mengembalikan potongan yang relevan. Kalau ya, tapi jawabannya tetap melenceng, masalahnya di instruksi — persis kegagalan ketiga yang dibahas di 5.1.

Memeriksa dari baris perintah

Selain Studio, Mastra menyediakan CLI untuk memeriksa trace tanpa membuka peramban:

bash
npx mastra api trace list

Perintah ini memerlukan server Mastra yang berjalan. Jalankan npx mastra dev, atau berikan URL server yang bisa dijangkau lewat --url.

Ini berguna saat kamu mendiagnosis di lingkungan tanpa antarmuka grafis, atau saat ingin menyaring trace dengan perkakas baris perintah biasa.

Kesalahan umum

Gejala: Biaya platform observability melonjak setelah aplikasi masuk produksi, padahal saat pengembangan terasa wajar. Penyebab: Sampling masih pada nilai bawaan ALWAYS, yang merekam semua span dari semua permintaan. Volume produksi berbeda beberapa orde besaran dari volume pengembangan. Perbaikan: Turunkan sampling, buang jenis span yang tidak kamu pakai lewat excludeSpanTypes, dan pertimbangkan spanFilter yang menyimpan semua trace gagal tapi hanya sebagian trace sukses.

Gejala: Ada ribuan trace tersimpan, tapi saat seorang pengguna melapor masalah, kamu tidak bisa menemukan trace miliknya. Penyebab: Tidak ada metadata yang menghubungkan trace ke pengguna atau tenant. Perbaikan: Isi requestContextKeys dengan kunci seperti userId dan tenantId, dan pastikan nilai-nilai itu memang diisi ke RequestContext di sisi server — caranya di 9.2.

Coba sendiri

Tantangan: Aktifkan tracing pada agent yang sudah kamu bangun, lalu pakai trace itu untuk menemukan masalah yang sengaja kamu tanam.

Ketentuan:

  1. Aktifkan observability dengan default: { enabled: true } pada agent yang punya minimal tiga tool
  2. Jalankan lima pertanyaan berbeda dan periksa trace-nya di Studio
  3. Untuk satu trace, catat: berapa kali model dipanggil, tool apa saja yang dipanggil, dan span mana yang paling lama
  4. Tanam masalah: tambahkan penundaan 2 detik di dalam salah satu tool, dan buat description dua tool lain menjadi mirip sehingga sulit dibedakan
  5. Jalankan lagi lima pertanyaan yang sama, dan temukan kedua masalah itu dari trace saja — tanpa melihat kode yang kamu ubah
  6. Tambahkan requestContextKeys untuk userId, jalankan dengan dua nilai berbeda, dan pastikan kamu bisa memfilter trace per pengguna
  7. Periksa apakah ada data yang tersensor di trace, dan cari tahu field mana

Checklist penerimaan:

  • [ ] Kamu bisa membaca hierarki span dan menjelaskan urutan eksekusinya
  • [ ] Penundaan 2 detik terlihat jelas sebagai span yang menonjol
  • [ ] Kamu bisa menunjukkan dari trace bahwa tool yang salah dipanggil — dengan melihat argumennya, bukan hanya jawabannya
  • [ ] Trace bisa difilter berdasarkan userId
  • [ ] Kamu bisa menyebutkan minimal satu field yang tersensor otomatis
  • [ ] Kamu bisa menyebutkan satu jenis span yang akan kamu buang di produksi, beserta alasannya

Petunjuk: Langkah 5 adalah inti latihan ini, dan lebih sulit dari kelihatannya. Kamu tahu masalahnya karena kamu yang menanamnya — tantangannya adalah menemukan jejaknya di trace, karena begitulah kondisi sebenarnya saat bug datang dari orang lain. Untuk masalah deskripsi tool yang mirip, jejaknya bukan di durasi, melainkan di argumen dan nama tool yang dipilih.

Ikhtisar

  • Trace merekam tiap operasi sebagai span yang tersusun hierarkis — membuka kotak hitam loop agent.
  • Aktifkan dengan observability: new Observability({ ... }) dari @mastra/observability — properti ini menerima instance, bukan objek konfigurasi. default: { enabled: true } untuk cepat, configs untuk kendali penuh.
  • requestContextKeys mengambil nilai seperti userId dan tenantId sebagai metadata — inilah yang membuat trace bisa dicari di produksi.
  • Tiga tuas pengurang biaya, diterapkan berurutan: excludeSpanTypesspanOutputProcessorsspanFilter. Sampling default-nya ALWAYS.
  • Pola produksi yang baik: simpan semua trace gagal, sebagian kecil trace sukses.
  • sensitiveDataFilter menyala secara default. Sesuaikan field-nya kalau perlu, tapi berpikirlah dua kali sebelum mematikannya.
  • Trace bisa diperiksa lewat Studio maupun npx mastra api trace list.

Lanjut ke mana

Trace memperlihatkan struktur eksekusi. Untuk detail di dalam tiap langkah — dan untuk angka agregat lintas ribuan permintaan — kamu butuh dua hal lain: 8.2 Logging & Metrics.

Materi belajar mandiri. Bukan dokumentasi resmi Mastra.