Sepanjang Section ini kita menempa alat demi alat: membaca traceback, bermain dengan debugger, lalu membangun logging yang berjenjang dan terstruktur. Alat-alat tersebut adalah bagian dari disiplin yang lebih besar bernama observability — seberapa baik kondisi internal sebuah sistem bisa dipahami dari keluarannya. Program yang observable tidak menunggu pengguna komplain; engineer-nya tahu lebih dulu bahwa error naik, request melambat, atau fitur tertentu berhenti dipakai. Pada Chapter penutup ini kita akan memetakan tiga pilar observability, menetapkan apa yang layak dan tidak layak dicatat, lalu merangkum praktik produksi menjadi satu checklist.
Tiga Pilar Observability #
Observability lazim dibangun di atas tiga jenis keluaran yang saling melengkapi:
| Pilar | Bentuk | Menjawab pertanyaan |
|---|---|---|
| Logs | kejadian diskrit, satu baris per event | “apa yang terjadi pada pesanan 4128?” |
| Metrics | angka agregat dari waktu ke waktu | “berapa laju error per menit selama seminggu?” |
| Traces | rantai panggilan antar service untuk satu request | “request ini lambat di tahap mana?” |
Logs yang kita bangun sepanjang Section ini unggul menjawab pertanyaan detail — isi variabel, konteks kegagalan, jejak lengkap satu kejadian. Metrics unggul untuk tren dan peringatan dini: satu angka “error rate 2,1%” lebih cepat dibaca daripada seribu baris error. Traces menyatukan keduanya lintas batas service: satu request yang menembus API, database, dan sistem pembayaran terpetakan sebagai satu rantai berdurasi per tahap.
Untuk program Python tunggal, logs adalah pilar utama yang realistis dijangkau standard library. Metrics tersedia lewat paket seperti prometheus-client, dan ketiga pilar sekaligus berada dalam cakupan OpenTelemetry — kumpulan API dan SDK standar industri (dengan dukungan Python yang matang) untuk menginstrumenkan aplikasi tanpa terikat satu vendor monitoring tertentu.
Apa yang Perlu Dicatat #
Isi log yang sehat bisa diringkas dalam tiga kategori: alur, kegagalan, dan keputusan. Simpan kode berikut sebagai muat_konfigurasi.py — contoh ini memperlihatkan ketiga kategori tersebut pada satu function pemuat konfigurasi:
import json
import logging
import sys
logging.basicConfig(
level=logging.INFO,
format="%(levelname)s %(name)s %(message)s",
stream=sys.stdout,
)
logger = logging.getLogger("toko.konfigurasi")
def muat_konfigurasi(path):
logger.info("memuat konfigurasi dari %s", path)
try:
with open(path, encoding="utf-8") as f:
return json.loads(f.read())
except FileNotFoundError:
logger.exception("file konfigurasi tidak ditemukan")
raise
try:
muat_konfigurasi("pengaturan.json")
except FileNotFoundError:
print("Aplikasi berhenti: konfigurasi wajib ada")Output yang Dihasilkan:
INFO toko.konfigurasi memuat konfigurasi dari pengaturan.json
ERROR toko.konfigurasi file konfigurasi tidak ditemukan
Traceback (most recent call last):
File "muat_konfigurasi.py", line 17, in muat_konfigurasi
with open(path, encoding="utf-8") as f:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'pengaturan.json'
Aplikasi berhenti: konfigurasi wajib adaMari kita bedah praktik baik di dalamnya:
- Baris 15 (
logger.info(...)): catatan masuk function — alur “apa yang sedang dikerjakan”. - Baris 20 (
logger.exception(...)): kegagalan dicatat lengkap dengan traceback-nya secara otomatis olehlogger.exception(), yang setara denganlogger.error(..., exc_info=True)dan hanya sah dipanggil dari dalam blokexcept. - Baris 21 (
raise): setelah dicatat, exception tetap diteruskan — logging bukan penanganan error; keputusan “apakah aplikasi boleh lanjut” tetap milik pemanggilnya. - Baris 5-9 (
logging.basicConfig(...)): format ringkas tanpa timestamp membuat contoh ini fokus; di produksi tambahkan%(asctime)sdan kirim ke handler berkas.
Sedikit catatan gaya: perhatikan pemanggilan logger.info("... %s", path) yang memakai parameter terpisah alih-alih f-string. Module logging menunda perangkaian string sampai pesan benar-benar lolos saringan level — pesan DEBUG yang tersaring tidak perlu membayar biaya format string.
Level di Produksi dan Sampling #
Aturan umum level untuk aplikasi yang melayani pengguna: produksi berjalan di INFO, lingkungan pengembangan boleh DEBUG. Dari kejadian ERROR yang tercatat, bangun metric dan peringatan (alert) — error yang hanya nongol di file log tanpa ada yang membacanya bukanlah observability. Level DEBUG di produksi biasanya hanya dinyalakan sementara saat menyelidiki masalah, lalu dikembalikan.
Program dengan traffic tinggi menambahkan teknik sampling: hanya sebagian request yang dicatat lengkap — misalnya satu dari seratus — agar volume log terkendali. Praktik umumnya: sampling diterapkan pada pesan INFO yang repetitif, sementara ERROR dan WARNING selalu dicatat penuh, dan event yang membawa correlation ID menyimpan satu baris pembuka per request tanpa kecuali.
Checklist Observability Praktis #
Sebelum aplikasi dinyatakan siap beroperasi, telusuri daftar periksa ini:
- Setiap module memakai
logger = logging.getLogger(__name__), bukanprint(). - Level produksi
INFO, lingkungan pengembanganDEBUG, diatur dari satu titik konfigurasi (env var ataudictConfig). - Kejadian penting (entry/exit use case, keputusan bisnis, kegagalan) tercatat; kegagalan membawa traceback via
logger.exception(). - Tidak ada rahasia, token, atau data pribadi dalam pesan log.
- Setiap request membawa correlation ID yang tercetak di seluruh baris lognya.
- Format log terstruktur (satu JSON per baris) bila log dituangkan ke sistem aggregation.
- Ada metric kunci minimal: jumlah request, laju error, dan latensi.
- Seseorang bertanggung jawab menerima alert dari metric tersebut — bukan hanya menyimpan datanya.
Dari traceback yang menakutkan pada Chapter Membaca Traceback hingga checklist ini, kita telah menempuh jalur lengkapnya: dari memperbaiki bug menjadi mencegah kekacauan, dan dari print() yang berserakan menjadi sistem yang bisa diandalkan.