Modul logging menyediakan pencatatan kejadian (logging) yang terstruktur sebagai pengganti debugging dengan print. Pencatatan dengan print menyebarkan cetakan tanpa tingkat keparahan, tanpa penanda asal pesan, dan harus dihapus satu per satu setelah masalah selesai. Modul logging memisahkan pesan dari kebijakan tampilannya, sehingga level yang ditampilkan, bentuk barisnya, dan tujuan penulisannya diatur dari satu tempat. Topik ini membahas lima level pencatatan, tiga komponen utamanya, yaitu logger, handler, dan formatter, serta pencatatan ke berkas.
Level pencatatan dari debug sampai critical#
Modul logging menyusun pesan menurut tingkat keparahan yang disebut level, dan setiap pesan dicatat pada level tertentu. Secara default, hanya pesan berlevel WARNING ke atas yang ditampilkan, sehingga pesan pembuka yang tidak penting tidak memenuhi layar. Perilaku default ini terlihat dari contoh berikut yang berjalan tanpa konfigurasi apa pun.
import logging
logging.info("pesan tingkat info tidak muncul")
logging.warning("pesan tingkat warning muncul")WARNING:root:pesan tingkat warning munculPesan tingkat INFO tidak tercetak karena berada di bawah ambang default, sedangkan pesan WARNING tampil dengan awalan root yang menandakan logger utamanya. Fungsi basicConfig() mengatur ambang tersebut, dan penurunan ambangnya ke DEBUG memunculkan kelima level sekaligus.
| Level | Nilai | Kapan dipakai |
|---|---|---|
DEBUG | 10 | detail teknis untuk diagnosis |
INFO | 20 | kejadian normal yang patut dicatat |
WARNING | 30 | kejadian tak terduga, tetapi program berjalan lanjut |
ERROR | 40 | satu operasi gagal |
CRITICAL | 50 | kegagalan total yang menghentikan program |
import logging
logging.basicConfig(level=logging.DEBUG, format="%(levelname)s %(message)s")
logging.debug("nilai variabel x = 42")
logging.info("proses selesai tanpa kendala")
logging.warning("kolom kosong pada baris 7")
logging.error("berkas konfigurasi tidak ditemukan")
logging.critical("koneksinya terputus")DEBUG nilai variabel x = 42
INFO proses selesai tanpa kendala
WARNING kolom kosong pada baris 7
ERROR berkas konfigurasi tidak ditemukan
CRITICAL koneksinya terputusParameter level menetapkan ambang minimum, sedangkan parameter format menentukan bentuk tiap baris, dan keduanya diatur sekali di awal program. Dengan ambang DEBUG, kelima pesan tampil berurutan sesuai urutan pemanggilannya, dan penamaan levelnya konsisten dipakai lintas pustaka pihak ketiga, sehingga log dari semuanya dapat dibaca dengan cara yang sama.
Logger, handler, dan formatter#
Konfigurasi lewat basicConfig() mengatur logger utama bernama root, sedangkan program yang lebih besar menggunakan logger bernama yang dibuat dengan getLogger("nama"). Nama bertitik seperti aplikasi.data membentuk hierarki, sehingga pesan modul data dapat dibedakan dari pesan modul lain. Setiap pesan kemudian melewati handler yang memutuskan tujuannya, lalu formatter milik handler yang membentuk teksnya, sebagaimana dirangkum diagram berikut.
Pada diagram tersebut, logger memeriksa level pesan terhadap ambangnya, handler memeriksa ulang terhadap ambangnya sendiri, dan formatter menyusun baris akhir sebelum teksnya ditulis ke target. Contoh berikut memasang satu logger bernama dengan satu handler layar yang ambangnya berbeda dari ambang logger.
import logging
logger = logging.getLogger("aplikasi.data")
logger.setLevel(logging.DEBUG)
layar = logging.StreamHandler()
layar.setLevel(logging.INFO)
layar.setFormatter(logging.Formatter("%(levelname)s %(name)s %(message)s"))
logger.addHandler(layar)
logger.debug("pesan tingkat debug, ditolak handler")
logger.info("pemuatan data dimulai")
logger.error("satu berkas gagal dibaca")INFO aplikasi.data pemuatan data dimulai
ERROR aplikasi.data satu berkas gagal dibacaPesan DEBUG lolos dari logger karena ambangnya DEBUG, tetapi ditolak handler yang ambangnya INFO, sehingga pemfilteran berlangsung dua lapis. Placeholder %(levelname)s, %(name)s, dan %(message)s diisi oleh formatter untuk setiap pesan, dan placeholder lain seperti %(asctime)s menambahkan penanda waktu pada baris yang sama.
Pencatatan ke berkas log#
Handler FileHandler menulis pesan ke berkas dengan formatter sendiri, sehingga riwayat kejadian tersimpan setelah program selesai. Parameter mode="w" memulai berkas baru pada setiap eksekusi, sedangkan mode="a" menambahkan ke berkas yang ada. Contoh berikut menulis dua pesan ke catatan.log, menutup handlernya, lalu membaca kembali isi berkas untuk ditampilkan.
import logging
logger = logging.getLogger("arsip")
logger.setLevel(logging.INFO)
berkas = logging.FileHandler("catatan.log", mode="w", encoding="utf-8")
berkas.setFormatter(logging.Formatter("%(levelname)s %(message)s"))
logger.addHandler(berkas)
logger.info("arsip dimulai")
logger.warning("satu berkas dilewati karena rusak")
berkas.close()
print(open("catatan.log", encoding="utf-8").read())INFO arsip dimulai
WARNING satu berkas dilewati karena rusakKeluaran layar hanya berasal dari print terakhir yang membaca isi catatan.log, sehingga baris yang tercetak memang berasal dari berkas, bukan dari handler layar. Dengan demikian, satu kejadian menghasilkan satu baris log persis sebagaimana formatter membentuknya. Untuk kebutuhan sederhana, basicConfig() juga menerima parameter filename yang memasang FileHandler secara langsung tanpa menyusun handler manual.
Kesalahan umum#
Kesalahan pertama adalah memanggil basicConfig() lebih dari sekali dengan harapan konfigurasinya berlaku. Fungsi ini hanya berpengaruh pada pemanggilan pertama, dan pemanggilan berikutnya diabaikan tanpa pesan.
import logging
logging.basicConfig(format="[pertama] %(message)s")
logging.basicConfig(format="[kedua] %(message)s")
logging.warning("pesan uji")[pertama] pesan ujiFormat kedua tidak berpengaruh karena handler root sudah terpasang pada pemanggilan pertama, dan parameter force=True pada Python 3.8 ke atas menghapus konfigurasi lama bila pengaturan ulang memang diinginkan. Kesalahan kedua adalah memasang handler baru setiap kali fungsi berjalan, sehingga satu pesan ditulis beberapa kali.
import logging
logger = logging.getLogger("duplikat")
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler())
logger.info("pesan pertama")
logger.addHandler(logging.StreamHandler())
logger.info("pesan kedua")pesan pertama
pesan kedua
pesan keduaSetiap handler memproses setiap pesan secara independen, sehingga dua handler menghasilkan dua baris untuk satu pemanggilan. Konfigurasi handler sebaiknya dilakukan satu kali di awal program, misalnya pada fungsi utama, dan logger di bagian lain cukup memanggil getLogger tanpa memasang apa pun. Kesalahan ketiga adalah menelan exception dalam blok except tanpa mencatat apa pun, sehingga kegagalan menghilang tanpa jejak.
# tidak-dijalankan: keluaran traceback memuat jalur berkas lokal
import logging
logger = logging.getLogger("pekerja")
try:
1 / 0
except ZeroDivisionError:
logger.exception("pembagian gagal")Metode logger.exception() mencatat pesan pada level ERROR beserta traceback lengkapnya, sehingga laporan kegagalan tersimpan seperti traceback yang tidak tertangani. Blok di atas tidak disertai keluaran karena tracebacknya memuat jalur berkas komputer tempat eksekusi berjalan, dan bentuk pesannya sudah dijelaskan pada topik traceback.
Ringkasan#
- Modul
loggingmenggantikan debugging denganprintmelalui level DEBUG, INFO, WARNING, ERROR, dan CRITICAL yang diurutkan dari 10 hingga 50. basicConfig()mengatur logger root satu kali, sedangkangetLogger("nama")membuat logger bernama yang berhierarki menurut titik pada namanya.- Handler menentukan target pesan seperti layar atau berkas, formatter menyusun teks tiap baris dari placeholder, dan level diperiksa berlapis pada logger dan handler sebelum pesan tampil.
- Handler sebaiknya dipasang satu kali,
logger.exception()merekam exception beserta tracebacknya, danFileHandlermenyimpan riwayat kejadian ke berkas.
Referensi#
- logging — Python Software Foundation — Referensi API modul logging untuk logger, handler, dan formatter.
- Logging HOWTO — Python Software Foundation — Panduan resmi pemakaian logging dari level dasar hingga konfigurasi lanjutan.