Skip to content

8.2 Logging & Metrics

Tujuan bab

Setelah bab ini kamu bisa:

  • Mengonfigurasi PinoLogger dan memilih level yang tepat
  • Menjelaskan bagaimana log otomatis terkorelasi dengan trace
  • Menghasilkan log JSON yang bisa diurai mesin
  • Mencatat log terstruktur dari dalam tool lewat observe
  • Membedakan kapan memakai log, trace, dan metrik

Prasyarat

Tiga alat, tiga pertanyaan

Trace, log, dan metrik sering disebut bersama sampai batas antaranya kabur. Pembedanya sederhana:

AlatMenjawabBentukVolume
TraceApa yang terjadi pada satu permintaan?Hierarki spanSatu per permintaan
LogDetail apa yang terjadi di dalam satu langkah?Baris peristiwaBanyak per permintaan
MetrikBagaimana perilakunya secara agregat?Angka teragregasiSatu angka untuk ribuan permintaan

Urutan pemakaian yang lazim saat mendiagnosis: metrik memberitahu ada masalah, trace memberitahu di mana, log memberitahu kenapa.

Mengonfigurasi logger

PinoLogger sudah disertakan secara default saat kamu membuat proyek Mastra lewat CLI:

ts
// src/mastra/index.ts
import { Mastra } from '@mastra/core/mastra'
import { PinoLogger } from '@mastra/loggers'

export const mastra = new Mastra({
  logger: new PinoLogger({
    name: 'Mastra',
    level: 'info',
  }),
})

Level yang tersedia mengikuti konvensi umum: debug, info, warn, error. Untuk pengembangan, debug berguna; untuk produksi, info biasanya titik awal yang wajar.

Log otomatis masuk ke observability

Ini perilaku yang layak diketahui karena menghemat pekerjaan.

Saat observability dikonfigurasi, semua pemanggilan logger otomatis diteruskan ke storage observability-mu. Setiap debug, info, warn, error, dan trackException — dari aplikasimu maupun dari komponen internal Mastra — tersimpan berdampingan dengan trace.

Dokumentasi menegaskan: tidak ada perubahan kode yang diperlukan.

Cara kerjanya berbeda tergantung logger:

  • Logger dengan dukungan adapter (PinoLogger dan ConsoleLogger) menulis ke tujuan mereka sendiri, lalu menurunkan log yang diekspor dari catatan yang sama
  • Logger kustom tanpa dukungan adapter dibungkus, sehingga menulis ke logger asli dan ke sistem observability sekaligus

Korelasi trace dan log

Ini fitur yang menyelesaikan masalah nyata dan sering luput.

Saat sebuah pemanggilan log terjadi di dalam operasi yang di-trace — run agent, langkah workflow, atau pemanggilan tool — Mastra menyisipkan trace_id dan span_id ke dalam keluaran native logger.

Field itu memakai format W3C trace context dan cocok dengan trace yang terlihat di Studio. Artinya kamu bisa mengambil satu baris log dari stdout dan langsung menemukan trace-nya.

Baris log di stdout                    Trace di Studio
{                                      ┌──────────────────┐
  "level": "error",          ────────▶ │ Trace abc123...  │
  "msg": "gagal ambil data",           │ └── Tool: fetch  │
  "trace_id": "abc123...",             │     ← span ini   │
  "span_id": "def456..."               └──────────────────┘
}

Tanpa korelasi ini, mendiagnosis error di produksi berarti mencocokkan timestamp secara manual — pekerjaan yang menjengkelkan dan sering keliru.

Log JSON untuk mesin

Pretty printing menyala secara default dan menghasilkan teks yang enak dibaca manusia. Untuk keluaran JSON yang bisa diurai mesin, matikan:

ts
// src/mastra/index.ts
import { Mastra } from '@mastra/core/mastra'
import { PinoLogger } from '@mastra/loggers'

export const mastra = new Mastra({
  logger: new PinoLogger({
    name: 'Mastra',
    level: 'info',
    prettyPrint: false,
  }),
})

Aturan praktisnya: prettyPrint: true di lokal, false di produksi. Sistem pengumpul log di produksi mengurai JSON; manusia yang menatap terminal tidak.

Mencatat dari dalam tool

Ingat dari 2.1 bahwa parameter kedua execute berisi konteks eksekusi. Salah satu isinya adalah observe, yang menyediakan pembantu observability.

ts
// src/mastra/tools/data-tool.ts
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'

export const dataTool = createTool({
  id: 'ambil-data',
  description: 'Ambil data penjualan untuk satu periode',
  inputSchema: z.object({ periode: z.string() }),
  outputSchema: z.object({ jumlah: z.number() }),
  execute: async ({ periode }, { observe }) => {
    observe.log('info', 'mulai ambil data', { periode })

    const jumlah = await observe.span('hitung-penjualan', async () => {
      return 42
    })

    observe.log('info', 'data diterima', { periode, jumlah })
    return { jumlah }
  },
})

observe.log adalah fungsi, bukan objek

Perhatikan bentuk pemanggilannya: observe.log('info', pesan, data) — level adalah argumen pertama.

Bentuk observe.log.info(...) yang terlihat wajar dan mengikuti kebiasaan pustaka logging lain akan ditolak compiler:

Property 'info' does not exist on type
'(level: "debug" | "info" | "warn" | "error" | "fatal",
  message: string, data?: Record<string, unknown>) => void'

Pesan error itu sekaligus memberitahu kelima level yang tersedia — perhatikan ada fatal, yang tidak ada pada level PinoLogger di bagian sebelumnya.

Dua kemampuan yang disediakan observe:

Kegunaan
observe.log(level, pesan, data?)Mencatat log terstruktur yang terkorelasi dengan trace
observe.span(nama, fn)Membungkus sebagian pekerjaan sebagai span anak

observe.span() berguna saat sebuah tool melakukan beberapa hal berbeda dan kamu ingin melihat masing-masing terpisah di trace — bukan satu span besar tanpa rincian.

Dokumentasi mencatat bahwa observe selalu tersedia — saat tidak ada konteks tracing yang aktif, span menjalankan fungsinya secara langsung dan log menjadi no-op. Artinya kamu tidak perlu memeriksa keberadaannya sebelum memakainya.

Manfaat memakai observe.log alih-alih console.log: log-nya terkorelasi dengan trace secara otomatis, dan ikut tersimpan di storage observability.

Catat data terstruktur, bukan kalimat

observe.log('info', 'data diterima', { periode, jumlah }) jauh lebih berguna daripada menggabungkan nilainya ke dalam string pesan. Yang pertama bisa difilter dan diagregasi; yang kedua hanya bisa dibaca.

Metrik

Metrik menjawab pertanyaan agregat yang tidak bisa dijawab satu trace: berapa rata-rata latensi minggu ini, berapa persen permintaan yang gagal, berapa token yang dipakai per pengguna.

Perbedaan mendasarnya dari trace: trace bagus untuk satu kejadian, metrik bagus untuk pola lintas ribuan kejadian. Kamu tidak akan menemukan bahwa latensi naik 30% sejak deployment kemarin dengan membaca trace satu per satu.

Seperti trace dan log, metrik disimpan di storage observability-mu dan bisa dilihat di Studio maupun lewat CLI.

Memeriksa dari baris perintah

bash
npx mastra api log list '{"level":"error","page":0,"perPage":50}'

Perintah ini mengambil log error terbaru tanpa membuka Studio. Ia memerlukan server Mastra yang berjalan dengan observability logging terkonfigurasi.

Untuk mengetahui bentuk filter yang tersedia:

bash
npx mastra api log list --schema

Pola --schema ini berlaku untuk perintah mastra api lainnya juga, dan berguna saat kamu tidak ingat bentuk argumen yang diminta.

Memilih alat yang tepat

Kesalahan umum

Gejala: Log di produksi sulit dicari dan tidak bisa difilter dengan perkakas pengumpul log. Penyebab: prettyPrint masih pada nilai bawaan true, sehingga keluarannya teks bebas alih-alih JSON. Perbaikan: Setel prettyPrint: false di produksi. Simpan true hanya untuk pengembangan lokal.

Gejala: Ada baris log error di stdout, tapi kamu tidak bisa menemukan trace yang bersangkutan di Studio. Penyebab: Log dihasilkan oleh console.log biasa alih-alih logger Mastra atau observe.log, sehingga trace_id dan span_id tidak ikut disisipkan. Perbaikan: Pakai logger Mastra di tingkat aplikasi, dan observe.log di dalam tool. Keduanya otomatis membawa konteks trace.

Coba sendiri

Tantangan: Bangun jalur diagnosis lengkap dari metrik ke log, lalu buktikan korelasi trace bekerja.

Ketentuan:

  1. Konfigurasikan PinoLogger dengan prettyPrint: false dan level info
  2. Tambahkan observe.log.info() di awal dan akhir tiap tool pada agent-mu, dengan data terstruktur, bukan kalimat yang digabung
  3. Tambahkan satu tool yang gagal secara acak sekitar sepertiga waktu, dan catat kegagalannya dengan observe.log.error() beserta konteksnya
  4. Jalankan agent 15 kali dengan pertanyaan yang bervariasi
  5. Ambil satu baris log error dari stdout, salin trace_id-nya, dan temukan trace itu di Studio
  6. Jalankan npx mastra api log list dengan filter level error, dan bandingkan dengan apa yang kamu lihat di stdout
  7. Ubah satu observe.log menjadi console.log biasa, jalankan lagi, dan perhatikan apa yang hilang

Checklist penerimaan:

  • [ ] Keluaran stdout berupa JSON yang memuat trace_id dan span_id
  • [ ] Kamu berhasil melompat dari satu baris log ke trace-nya di Studio
  • [ ] mastra api log list mengembalikan error yang sama dengan yang kamu lihat di stdout
  • [ ] Log dari langkah 7 tidak membawa trace_id, dan kamu bisa menjelaskan kenapa itu masalah
  • [ ] Semua log-mu memakai data terstruktur — tidak ada yang menggabung nilai ke dalam string pesan
  • [ ] Kamu bisa menjelaskan kapan kamu akan melihat metrik alih-alih trace

Petunjuk: Langkah 7 memperlihatkan nilai korelasi dengan cara yang paling meyakinkan: dengan menghilangkannya. Satu baris log tanpa trace_id di antara ratusan baris lain praktis adalah petunjuk buntu — kamu tahu ada yang gagal, tapi tidak tahu pada permintaan yang mana.

Ikhtisar

  • Trace untuk satu permintaan, log untuk detail di dalam langkah, metrik untuk pola agregat. Diagnosis biasanya berjalan: metrik → trace → log.
  • PinoLogger disertakan default oleh CLI. Level: debug, info, warn, error.
  • Log otomatis diteruskan ke storage observability saat observability dikonfigurasi — tanpa perubahan kode.
  • Log di dalam operasi yang di-trace otomatis membawa trace_id dan span_id dalam format W3C, sehingga bisa dilompati langsung ke trace-nya.
  • prettyPrint: false untuk produksi agar keluarannya JSON yang bisa diurai mesin.
  • Di dalam tool, pakai observe.log(level, pesan, data) dari parameter kedua execute — ia fungsi, bukan objek, jadi observe.log.info(...) tidak akan kompilasi. Tersedia juga observe.span(nama, fn) untuk membuat span anak. Keduanya selalu ada, dan menjadi no-op saat tidak ada tracing aktif.
  • Catat data terstruktur, bukan nilai yang digabung ke dalam kalimat.
  • CLI: npx mastra api log list, dan --schema untuk melihat bentuk filter.

Lanjut ke mana

Kamu sekarang bisa melihat apa yang terjadi. Dua bab terakhir bagian ini tentang menilai hasilnya: 8.3 Scorers Bawaan.

Materi belajar mandiri. Bukan dokumentasi resmi Mastra.