Pernah deploy aplikasi Python ke server, eh tiba-tiba error tapi gak ada log yang bisa dibaca? Atau worse, cuma pakai print() kemana-mana buat debug? Kalau kamu pernah ngalamin ini, tenang - kamu gak sendirian. Banyak developer yang ngeremehin logging sampai akhirnya kena masalah di production.
Python punya module bawaan namanya logging yang super powerful. Gak cuma buat nulis pesan ke console, tapi juga bisa rotate file otomatis, kirim alert ke email, bahkan integrate sama tools monitoring. Di tutorial ini, saya bakal jelasin dari dasar sampai pola yang dipakai di production.
Sebelum masuk ke logging, penting buat ngerti kenapa print() itu masalah di production:
Module logging solve semua masalah di atas. Dan yang paling penting, ini module bawaan Python - gak perlu install apapun.
Cara paling simpel buat mulai pakai logging:
import logging
# Setup basic configuration
logging.basicConfig(level=logging.INFO)
# Gunakan logging sebagai pengganti print
logging.info("Aplikasi dimulai")
logging.warning("Disk usage mencapai 80%")
logging.error("Database connection failed")
Outputnya bakal kayak gini:
INFO:root:Aplikasi dimulai
WARNING:root:Disk usage mencapai 80%
ERROR:root:Database connection failed
Langsung keliatan bedanya kan? Ada level (INFO, WARNING, ERROR) dan nama logger (root). Jauh lebih informatif dibanding print().
Python punya 5 level logging dari yang paling ringan sampai paling serius:
import logging
logging.debug("Info detail buat developer - biasanya cuma dipake pas debugging")
logging.info("Konfirmasi bahwa aplikasi berjalan normal")
logging.warning("Ada sesuatu yang gak biasa, tapi aplikasi masih jalan")
logging.error("Gagal menjalankan fungsi tertentu")
logging.critical("Aplikasi crash! Butuh perhatian segera!")
Level dari paling rendah ke paling tinggi:
Trik penting: di production, set level ke WARNING atau ERROR. Kalau set ke DEBUG, log file bakal penuh banget dan susah dicari yang penting.
Default format logging terlalu simpel. Buat production, kamu butuh format yang lebih lengkap:
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logging.info("User login berhasil")
logging.error("Payment gateway timeout")
Output:
2026-06-26 08:30:15 - root - INFO - User login berhasil
2026-06-26 08:30:16 - root - ERROR - Payment gateway timeout
Ada banyak variabel yang bisa dipake di format:
%(asctime)s - Timestamp lengkap%(name)s - Nama logger%(levelname)s - Nama level (INFO, ERROR, dll)%(levelno)s - Nomor level (20, 40, dll)%(filename)s - Nama file sumber%(funcName)s - Nama fungsi%(lineno)d - Nomor baris%(message)s - Pesan logDi production, log HARUS ditulis ke file. Console aja gak cukup karena kalau server restart, semua hilang:
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('app.log'),
logging.StreamHandler() # Tetap tampil di console juga
]
)
logging.info("Aplikasi berjalan di port 8000")
logging.error("Redis connection refused")
Sekarang log bakal ditulis ke file app.log DAN tampil di console. Pakai dua handler sekaligus - satu buat file, satu buat terminal.
Masalah kalau tulis log ke file terus-terusan: file bakal makin gede dan makan disk space. Solusinya pakai RotatingFileHandler:
import logging
from logging.handlers import RotatingFileHandler
# Setup rotating handler
handler = RotatingFileHandler(
'app.log',
maxBytes=5*1024*1024, # 5 MB per file
backupCount=3 # Simpan 3 file backup
)
handler.setFormatter(logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
))
logger = logging.getLogger('myapp')
logger.setLevel(logging.INFO)
logger.addHandler(handler)
# Log sebanyak apapun, file tetap terkontrol
for i in range(100000):
logger.info(f"Processing item {i}")
Dengan konfigurasi di atas:
app.log.1app.log dibuat lagiapp.log.1, app.log.2, app.log.3)Ada juga TimedRotatingFileHandler yang rotate berdasarkan waktu:
from logging.handlers import TimedRotatingFileHandler
handler = TimedRotatingFileHandler(
'app.log',
when='midnight', # Rotate setiap tengah malam
interval=1,
backupCount=30 # Simpan 30 hari terakhir
)
Kalau mau belajar lebih lanjut tentang task scheduling di Python, cek tutorial Task Scheduler Python dengan Schedule dan APScheduler yang sudah saya tulis sebelumnya.
Di aplikasi yang besar, jangan pakai logging.info() langsung. Buat custom logger per module:
# database.py
import logging
logger = logging.getLogger(__name__)
def connect():
logger.info("Connecting to database...")
try:
# kode koneksi database
logger.info("Database connected successfully")
except Exception as e:
logger.error(f"Database connection failed: {e}")
raise
# api.py
import logging
logger = logging.getLogger(__name__)
def fetch_data():
logger.info("Fetching data from external API")
# kode fetch data
Keuntungan pakai __name__:
database, api)Satu fitur logging yang sering dilupain: exc_info=True. Ini bikin log otomatis include traceback:
import logging
logging.basicConfig(level=logging.INFO)
try:
result = 10 / 0
except ZeroDivisionError:
logging.error("Terjadi error saat menghitung", exc_info=True)
Output:
ERROR:root:Terjadi error saat menghitung
Traceback (most recent call last):
File "app.py", line 5, in
result = 10 / 0
ZeroDivisionError: division by zero
Tanpa exc_info=True, kamu cuma dapat pesan errornya aja tanpa traceback. Ini krusial buat debugging di production.
Buat lebih simpel, bisa pakai logger.exception() yang otomatis include traceback:
try:
process_data()
except Exception:
logger.exception("Gagal memproses data") # exc_info=True otomatis
Buatan production, lebih baik pakai file konfigurasi daripada hardcode di Python. Pakai dictConfig:
import logging.config
LOGGING_CONFIG = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
},
'detailed': {
'format': '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'standard',
'level': 'INFO',
},
'file': {
'class': 'logging.handlers.RotatingFileHandler',
'filename': 'app.log',
'maxBytes': 10485760, # 10 MB
'backupCount': 5,
'formatter': 'detailed',
'level': 'DEBUG',
},
'error_file': {
'class': 'logging.handlers.RotatingFileHandler',
'filename': 'error.log',
'maxBytes': 5242880, # 5 MB
'backupCount': 3,
'formatter': 'detailed',
'level': 'ERROR',
},
},
'loggers': {
'': { # Root logger
'handlers': ['console', 'file', 'error_file'],
'level': 'DEBUG',
},
'database': {
'handlers': ['file', 'error_file'],
'level': 'WARNING',
'propagate': False,
},
},
}
logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger(__name__)
Dengan konfigurasi ini:
app.log catet semua sampai DEBUGerror.log khusus ERROR dan CRITICALdatabase cuma log WARNING ke atas (biar gak noisy)Kalau kamu juga pakai Python buat web scraping, pola logging ini sangat berguna buat monitor proses scraping. Cek juga tutorial Web Scraping dengan Python dan BeautifulSoup untuk contoh implementasinya.
Setelah bertahun-tahun pakai Python logging di production, ini pola yang paling works:
1. Pisahkan log per severity:
# Semua log ke app.log
# Error aja ke error.log
# Access log terpisah (buat web app)
2. Jangan log data sensitif:
# JANGAN begini
logger.info(f"User login: {username}, password: {password}")
# BENER begini
logger.info(f"User login: {username}")
3. Pakai structured logging buat production:
import json
def json_formatter(record):
log_data = {
'timestamp': record.created,
'level': record.levelname,
'message': record.getMessage(),
'module': record.module,
'function': record.funcName,
'line': record.lineno,
}
if record.exc_info:
log_data['exception'] = logging.Formatter().formatException(record.exc_info)
return json.dumps(log_data)
Structured logging (format JSON) jauh lebih gampang diproses sama tools monitoring kayak ELK Stack, Datadog, atau Grafana Loki.
4. Set level dinamis via environment variable:
import os
import logging
log_level = os.getenv('LOG_LEVEL', 'INFO').upper()
logging.basicConfig(level=getattr(logging, log_level))
Begitu production, tinggal set LOG_LEVEL=WARNING di environment variable tanpa ubah kode.
Biar lebih praktis, ini contoh logging di aplikasi Flask sederhana:
import logging
from logging.handlers import RotatingFileHandler
from flask import Flask, request
app = Flask(__name__)
# Setup logging
handler = RotatingFileHandler('flask_app.log', maxBytes=10000000, backupCount=5)
handler.setLevel(logging.INFO)
handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
))
app.logger.addHandler(handler)
app.logger.setLevel(logging.INFO)
@app.route('/api/data')
def get_data():
app.logger.info(f"Request dari {request.remote_addr} ke /api/data")
try:
# Proses data
result = process_data()
app.logger.info(f"Data berhasil diproses: {len(result)} items")
return {'data': result}
except Exception:
app.logger.exception("Gagal memproses data")
return {'error': 'Internal server error'}, 500
if __name__ == '__main__':
app.logger.info("Flask app starting...")
app.run(host='0.0.0.0', port=5000)
Logging itu investasi yang hasilnya baru keliatan pas aplikasi kamu jalan di production. Mulai sekarang, ganti semua print() dengan logging. Config yang ideal:
Python module logging itu flexible banget - dari yang simpel sampe yang advanced kayak structured logging dan custom handler. Mulai dari yang basic dulu, terus upgrade sesuai kebutuhan.
Pertanyaan: aplikasi Python kamu sekarang udah pakai logging yang proper, atau masih mengandalkan print()? Share di komentar ya!