Programming

Python Logging untuk Aplikasi Production - Tutorial Module logging

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.

Kenapa Gak Cukup Pakai print?

Sebelum masuk ke logging, penting buat ngerti kenapa print() itu masalah di production:

  • Gak ada level severity - semua output terlihat sama, gak bisa bedain info, warning, dan error
  • Gak bisa di-configure - mau matiin print di production? Harus hapus manual satu-satu
  • Gak ada timestamp - kapan persisnya error terjadi? Print gak kasih tau
  • Gak tulis ke file - kalau server restart, semua log hilang
  • Performance buruk - print ke stdout itu blocking I/O yang lambat

Module logging solve semua masalah di atas. Dan yang paling penting, ini module bawaan Python - gak perlu install apapun.

Dasar-Dasar Module logging

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().

5 Level Logging yang Wajib Diketahui

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:

  • DEBUG (10) - Detail teknis yang cuma berguna pas development
  • INFO (20) - Konfirmasi bahwa sesuatu terjadi sesuai rencana
  • WARNING (30) - Ada potensi masalah tapi belum fatal
  • ERROR (40) - Gagal menjalankan operasi tertentu
  • CRITICAL (50) - Error serius yang bikin aplikasi gak bisa jalan

Trik penting: di production, set level ke WARNING atau ERROR. Kalau set ke DEBUG, log file bakal penuh banget dan susah dicari yang penting.

Format Log yang Profesional

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 log

Tulis Log ke File

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

Rotating File Handler - Log Gak Bakal Penuh

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:

  • File log maksimal 5 MB
  • Kalau udah 5 MB, file di-rename jadi app.log.1
  • File baru app.log dibuat lagi
  • Maksimal 3 backup (app.log.1, app.log.2, app.log.3)
  • File ke-4 otomatis dihapus

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.

Custom Logger untuk Setiap Module

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__:

  • Logger otomatis pakai nama module (contoh: database, api)
  • Bisa set level berbeda per module
  • Gampang trace error dari module mana

Exception Handling dengan exc_info

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

Konfigurasi logging dari File

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:

  • Console cuma tampilin INFO ke atas
  • File app.log catet semua sampai DEBUG
  • File error.log khusus ERROR dan CRITICAL
  • Module database 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.

Best Practice Logging di Production

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.

Contoh Implementasi di Aplikasi Flask

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)

Kesimpulan

Logging itu investasi yang hasilnya baru keliatan pas aplikasi kamu jalan di production. Mulai sekarang, ganti semua print() dengan logging. Config yang ideal:

  • Development: level DEBUG ke console
  • Staging: level INFO ke file + console
  • Production: level WARNING ke file (dengan rotating), ERROR ke file terpisah

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!


You may also like


0 Comments


Leave a Reply

Comments with links or spam keywords will be rejected.
Scroll to Top