logging

Modul logging dengan level, handler, dan formatter sebagai pengganti print debugging.

Daftar isi

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.

python Contoh: perilaku default tanpa konfigurasi
import logging

logging.info("pesan tingkat info tidak muncul")
logging.warning("pesan tingkat warning muncul")
text Keluaran
WARNING:root:pesan tingkat warning muncul

Pesan 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.

LevelNilaiKapan dipakai
DEBUG10detail teknis untuk diagnosis
INFO20kejadian normal yang patut dicatat
WARNING30kejadian tak terduga, tetapi program berjalan lanjut
ERROR40satu operasi gagal
CRITICAL50kegagalan total yang menghentikan program
python Contoh: lima level dengan basicConfig
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")
text Keluaran
DEBUG nilai variabel x = 42
INFO proses selesai tanpa kendala
WARNING kolom kosong pada baris 7
ERROR berkas konfigurasi tidak ditemukan
CRITICAL koneksinya terputus

Parameter 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.

Loggeraplikasi.datasetLevel(DEBUG)HandlerStreamHandlerFileHandlerFormatter%(levelname)s%(message)starget keluaran: stderr atau berkas log
Alur sebuah pesan logging dari logger melalui handler menuju formatter dan keluarannya

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.

python Contoh: logger bernama dengan handler dan formatter
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")
text Keluaran
INFO aplikasi.data pemuatan data dimulai
ERROR aplikasi.data satu berkas gagal dibaca

Pesan 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.

python Contoh: mencatat ke berkas log
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())
text Keluaran
INFO arsip dimulai
WARNING satu berkas dilewati karena rusak

Keluaran 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.

python Contoh: basicConfig hanya berlaku pada pemanggilan pertama
import logging

logging.basicConfig(format="[pertama] %(message)s")
logging.basicConfig(format="[kedua] %(message)s")
logging.warning("pesan uji")
text Keluaran
[pertama] pesan uji

Format 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.

python Contoh: handler terpasang dua kali menggandakan baris
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")
text Keluaran
pesan pertama
pesan kedua
pesan kedua

Setiap 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.

python Contoh: merekam exception dengan logger.exception
# 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 logging menggantikan debugging dengan print melalui level DEBUG, INFO, WARNING, ERROR, dan CRITICAL yang diurutkan dari 10 hingga 50.
  • basicConfig() mengatur logger root satu kali, sedangkan getLogger("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, dan FileHandler menyimpan riwayat kejadian ke berkas.

Referensi#