🐍 کتابخانه مرجع پایتون

📝 لاگ‌نویسی (Logging) در پایتون

لاگ‌نویسی یکی از مهم‌ترین مهارت‌ها در توسعه نرم‌افزار است که به شما امکان می‌دهد رویدادها، خطاها و اطلاعات برنامه را ثبت کنید. ماژول logging در پایتون یک سیستم استاندارد و قدرتمند برای ثبت وقایع ارائه می‌دهد که به شما کمک می‌کند برنامه خود را دیباگ کنید و عملکرد آن را مانیتور کنید.

🔹 تنظیمات پایه و سطوح لاگ

import logging

# تنظیمات پایه
logging.basicConfig(
    level=logging.DEBUG,  # سطح لاگ
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# تست سطوح مختلف
logging.debug("This is a DEBUG message")
logging.info("This is an INFO message")
logging.warning("This is a WARNING message")
logging.error("This is an ERROR message")
logging.critical("This is a CRITICAL message")

🔹 سطوح لاگ (Log Levels)

سطح مقدار کاربرد
DEBUG 10 اطلاعات دقیق برای دیباگ
INFO 20 اطلاعات عمومی از اجرای برنامه
WARNING 30 هشدارها (برنامه همچنان کار می‌کند)
ERROR 40 خطاهای جدی (برنامه با مشکل مواجه شده)
CRITICAL 50 خطاهای بحرانی (برنامه ممکن است متوقف شود)

🔹 فرمت‌بندی لاگ‌ها

import logging

# فرمت‌های مختلف
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)-8s - %(name)-15s - %(message)s',
    datefmt='%H:%M:%S'
)

logger = logging.getLogger('my_app')
logger.info("Application started")
logger.warning("Low memory warning")

# فرمت با کدهای رنگی (برای ترمینال)
# می‌توانید از کتابخانه‌های colorlog استفاده کنید

🔹 نوشتن لاگ در فایل

import logging

# تنظیم لاگ در فایل
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler("app.log", encoding='utf-8'),  # ذخیره در فایل
        logging.StreamHandler()  # نمایش در کنسول
    ]
)

logger = logging.getLogger(__name__)
logger.info("This message goes to both file and console")
logger.error("Error message with timestamp")

# نوشتن در فایل جداگانه برای خطاها
error_handler = logging.FileHandler("errors.log")
error_handler.setLevel(logging.ERROR)
error_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))

logger.addHandler(error_handler)
logger.error("This error goes to errors.log")

🔹 Loggerهای مختلف

import logging

# تنظیمات پایه
logging.basicConfig(level=logging.DEBUG)

# Logger برای ماژول‌های مختلف
logger1 = logging.getLogger('module1')
logger2 = logging.getLogger('module2')

logger1.info("This is module1 message")
logger2.warning("This is module2 warning")

# تنظیم سطح متفاوت برای هر logger
logger1.setLevel(logging.WARNING)
logger2.setLevel(logging.DEBUG)

logger1.debug("This won't show (level WARNING)")
logger2.debug("This will show (level DEBUG)")

# استفاده از logger با سلسله‌مراتب
parent = logging.getLogger('app')
child = logging.getLogger('app.module')

parent.info("Parent logger")
child.info("Child logger (inherits parent settings)")

🔹 لاگ‌کردن استثناها (Exceptions)

import logging

logging.basicConfig(level=logging.ERROR)

logger = logging.getLogger(__name__)

try:
    result = 10 / 0
except ZeroDivisionError as e:
    # لاگ کامل استثنا با traceback
    logger.error("Division by zero occurred", exc_info=True)
    # یا
    logger.exception("Division by zero occurred")  # خودکار exc_info=True

try:
    int("abc")
except ValueError:
    # لاگ بدون traceback
    logger.error("Invalid conversion", exc_info=False)

🔹 پیکربندی پیشرفته (dictConfig)

import logging
import logging.config

# پیکربندی با دیکشنری
LOGGING_CONFIG = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
        },
        'detailed': {
            'format': '%(asctime)s - %(name)s - %(levelname)s - %(module)s - %(message)s'
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',
            'formatter': 'standard'
        },
        'file': {
            'class': 'logging.FileHandler',
            'filename': 'app.log',
            'level': 'INFO',
            'formatter': 'detailed',
            'encoding': 'utf-8'
        }
    },
    'loggers': {
        'app': {
            'level': 'DEBUG',
            'handlers': ['console', 'file'],
            'propagate': False
        },
        'app.module1': {
            'level': 'INFO',
            'handlers': ['console'],
            'propagate': False
        }
    },
    'root': {
        'level': 'WARNING',
        'handlers': ['console']
    }
}

logging.config.dictConfig(LOGGING_CONFIG)

logger = logging.getLogger('app')
logger.debug("Debug message from app")
logger.info("Info message from app")

module1_logger = logging.getLogger('app.module1')
module1_logger.info("Info from module1")
module1_logger.debug("Debug from module1 (won't show)")

🔹 چرخش لاگ (RotatingFileHandler)

import logging
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler

# RotatingFileHandler: چرخش بر اساس حجم
rotating_handler = RotatingFileHandler(
    "app.log",
    maxBytes=1000,  # حداکثر حجم هر فایل (بایت)
    backupCount=3  # تعداد فایل‌های پشتیبان
)
rotating_handler.setLevel(logging.DEBUG)
rotating_handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))

logger = logging.getLogger('rotating')
logger.addHandler(rotating_handler)

# TimedRotatingFileHandler: چرخش بر اساس زمان
timed_handler = TimedRotatingFileHandler(
    "app_timed.log",
    when="midnight",  # هر روز نیمه‌شب
    backupCount=7  # ۷ روز پشتیبان
)
timed_handler.setLevel(logging.INFO)
timed_handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))

logger.addHandler(timed_handler)
logger.info("This log will rotate daily")

🔹 مثال جامع: سیستم لاگ‌گیری برنامه

import logging
import logging.config
import datetime

# پیکربندی لاگ
LOGGING_CONFIG = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'simple': {
            'format': '%(asctime)s - %(levelname)-8s - %(message)s'
        },
        'detailed': {
            'format': '%(asctime)s - %(name)s - %(levelname)-8s - %(module)s:%(lineno)d - %(message)s'
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',
            'formatter': 'simple'
        },
        'app_file': {
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': 'app.log',
            'maxBytes': 1024*1024,  # 1 MB
            'backupCount': 5,
            'level': 'INFO',
            'formatter': 'detailed',
            'encoding': 'utf-8'
        },
        'error_file': {
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': 'errors.log',
            'maxBytes': 1024*1024,
            'backupCount': 3,
            'level': 'ERROR',
            'formatter': 'detailed',
            'encoding': 'utf-8'
        }
    },
    'loggers': {
        'app': {
            'level': 'DEBUG',
            'handlers': ['console', 'app_file'],
            'propagate': False
        },
        'app.errors': {
            'level': 'ERROR',
            'handlers': ['error_file'],
            'propagate': False
        }
    },
    'root': {
        'level': 'WARNING',
        'handlers': ['console']
    }
}

logging.config.dictConfig(LOGGING_CONFIG)

class AppLogger:
    """کلاس مدیریت لاگ‌های برنامه"""
    
    def __init__(self, name):
        self.logger = logging.getLogger(f'app.{name}')
        self.error_logger = logging.getLogger('app.errors')
    
    def debug(self, message):
        self.logger.debug(message)
    
    def info(self, message):
        self.logger.info(message)
    
    def warning(self, message):
        self.logger.warning(message)
    
    def error(self, message, exc_info=False):
        self.logger.error(message, exc_info=exc_info)
        self.error_logger.error(message, exc_info=exc_info)
    
    def critical(self, message):
        self.logger.critical(message)
        self.error_logger.critical(message)

# استفاده
logger = AppLogger('user_service')

logger.info("User service started")
logger.debug("Loading configuration...")
logger.warning("Database connection slow")

try:
    # شبیه‌سازی خطا
    user_data = {"id": 1, "name": "Saba"}
    if not user_data.get("email"):
        raise ValueError("Email is required")
except ValueError as e:
    logger.error(f"User validation error: {e}", exc_info=True)

logger.info("User service shutdown")

📌 نکات کلیدی

← قبلی: API با requests → بعدی: تست با unittest ↑ بازگشت به فهرست متوسط