Pipeline yang berjalan sebulan sekali tidak selalu diawasi seseorang. Ketika hasilnya dicurigai, pertanyaan pertama selalu sama: apa yang sebenarnya terjadi saat dijalankan? Pada bagian ini kita memasang logging pada seluruh tahap dengan modul logging bawaan Python. Level yang tepat dipilih per kejadian, keluaran diarahkan ke dua tempat sekaligus, dan formatnya dibuat seragam.
print atau logging #
Untuk pipeline produksi, print tidak memenuhi syarat:
| Aspek | print | logging |
|---|---|---|
| Level keparahan | tidak ada | DEBUG sampai ERROR |
| Waktu kejadian | harus dihitung sendiri | otomatis dari formatter |
| Tujuan keluaran | satu tempat | konsol dan berkas sekaligus |
| Menyaring saat bermasalah | mengubah kode | ganti level saja |
Satu baris log kita memuat tiga informasi: waktu kejadian, level, dan pesan. Baris WARNING yang sama tampil di layar operator dan tersimpan di data/processed/pipeline.log untuk ditelusuran minggu berikutnya.
setup_logger #
Satu fungsi di src/logging_setup.py menyiapkan semuanya:
FORMAT_LOG = "%(asctime)s | %(levelname)-8s | %(message)s"
FORMAT_WAKTU = "%Y-%m-%d %H:%M:%S"
def setup_logger(
nama: str = "pipeline",
berkas: str | None = None,
level: int = logging.INFO,
) -> logging.Logger:
"""Siapkan logger dengan handler konsol dan (opsional) handler berkas.
- nama : nama logger; ambil logger yang sama dengan nama yang sama.
- berkas : bila diisi, baris log juga ditulis ke berkas ini.
- level : batas level yang dicatat (default INFO).
"""
logger = logging.getLogger(nama)
logger.setLevel(level)
logger.propagate = False
logger.handlers.clear()
pemformat = logging.Formatter(FORMAT_LOG, datefmt=FORMAT_WAKTU)
konsol = logging.StreamHandler()
konsol.setFormatter(pemformat)
logger.addHandler(konsol)
if berkas:
Path(berkas).parent.mkdir(parents=True, exist_ok=True)
ke_berkas = logging.FileHandler(berkas, encoding="utf-8")
ke_berkas.setFormatter(pemformat)
logger.addHandler(ke_berkas)
return loggerTiga keputusan pada fungsi ini layak diperhatikan:
logger.handlers.clear(): panggilansetup_loggerkedua kali mengganti handler lama. Tanpa baris ini, menjalankan pipeline dua kali dalam satu proses akan menggandakan setiap baris log.logger.propagate = False: logger tidak meneruskan barisnya ke logger induk, sehingga tidak muncul dua kali lewat jalur root.- Dua handler, satu format: konsol untuk operator, berkas untuk arsip. Keduanya memakai
pemformatyang sama sehingga isinya identik.
Log di Sepanjang Pipeline #
Modul src/pipeline.py mencatat setiap tahap. Ringkasannya sebagai berikut:
| Tahap | Level | Baris log |
|---|---|---|
| Mulai proses | INFO | jumlah berkas sumber |
| Baris ditolak | WARNING | nama berkas, nomor baris, kode masalah, alasan |
| Ringkasan per berkas | INFO | dibaca, ditolak, lolos |
| Pembersihan | INFO | jumlah baris bersih |
| Duplikat dibuang | WARNING | jumlah persis dan konflik |
| Pemuatan | INFO | jumlah baris dan tujuan |
| Selesai | INFO | rekap akhir empat angka |
Level dipilih menurut pertanyaan yang dijawab, bukan selera. INFO menjawab “tahapan mana yang selesai”; WARNING menjawab “data mana yang tidak masuk dan mengapa”. ERROR hanya untuk keadaan yang memang menghentikan pipeline, misalnya header berkas tidak dikenal.
Berkas Log yang Terus Bertambah #
Handler berkas menambahkan baris baru pada setiap eksekusi; satu eksekusi penuh menambah 31 baris. Untuk latihan ini ukurannya kecil, tetapi pipeline produksi perlu pembatasan. Kebiasaan yang umum adalah rotasi: memakai RotatingFileHandler yang memecah berkas log berdasarkan ukuran, lalu menyimpan beberapa generasi terakhir. Kita memilih FileHandler polos agar contoh tetap kecil; bila ingin mencoba rotasi, cukup ganti satu baris di setup_logger.
Log Hasil Eksekusi Nyata #
Berkas data/processed/pipeline.log berikut adalah hasil eksekusi sungguhan. Dua belas baris pertama diperlihatkan:
head -12 data/processed/pipeline.logOutput yang Dihasilkan:
2026-10-17 07:42:21 | INFO | Memulai pipeline: 2 berkas sumber
2026-10-17 07:42:21 | WARNING | pesanan_2024-06.csv: baris 9 ditolak (email_tidak_valid): email harus berformat [email protected]
2026-10-17 07:42:21 | WARNING | pesanan_2024-06.csv: baris 14 ditolak (jumlah_tidak_positif): jumlah tidak boleh negatif
2026-10-17 07:42:21 | WARNING | pesanan_2024-06.csv: baris 20 ditolak (harga_kosong): harga tidak boleh kosong
2026-10-17 07:42:21 | WARNING | pesanan_2024-06.csv: baris 25 ditolak (tanggal_tidak_valid): tanggal harus tanggal nyata berformat YYYY-MM-DD
2026-10-17 07:42:21 | WARNING | pesanan_2024-06.csv: baris 32 ditolak (order_id_tidak_valid): pola order_id harus ORD-YYYY-NNNN
2026-10-17 07:42:21 | WARNING | pesanan_2024-06.csv: baris 38 ditolak (kategori_tidak_dikenal): kategori harus salah satu dari: buku, elektronik, fashion, makanan, olahraga
2026-10-17 07:42:21 | WARNING | pesanan_2024-06.csv: baris 43 ditolak (pelanggan_kosong): nama pelanggan tidak boleh kosong
2026-10-17 07:42:21 | WARNING | pesanan_2024-06.csv: baris 49 ditolak (tanggal_tidak_valid): tanggal harus tanggal nyata berformat YYYY-MM-DD
2026-10-17 07:42:21 | WARNING | pesanan_2024-06.csv: baris 54 ditolak (jumlah_tidak_positif): jumlah minimal 1
2026-10-17 07:42:21 | WARNING | pesanan_2024-06.csv: baris 60 ditolak (email_kosong): email tidak boleh kosong
2026-10-17 07:42:21 | WARNING | pesanan_2024-06.csv: baris 65 ditolak (produk_kosong): nama produk tidak boleh kosongSatu eksekusi lengkap menghasilkan 31 baris: 21 baris WARNING untuk penolakan, dan 10 baris INFO untuk tahapan. Waktu pada kolom pertama adalah waktu nyata eksekusi; eksekusi berikutnya mencatat waktu yang berbeda. Nomor baris pada pesan penolakan merujuk nomor baris berkas CSV, sehingga operator dapat membuka berkas sumber tepat ke baris tersebut.
Materi lanjutan tentang level, handler, dan penelusuran kejadian ada di chapter Logging dan Pipeline Logging. Rujukan resmi: dokumentasi modul logging Python .
Seluruh tahap kini menghasilkan jejak yang dapat ditelusuri. Tahap berikutnya memastikan jejak itu sesuai janji: kita menguji setiap lapisan dengan pytest sebelum menjalankan pipeline di data asli. Lanjutkan ke Bagian 8: Menguji Pipeline.