خانه / آموزش‌ها / آموزش جامع Logging در پایتون؛ خداحافظی با print

آموزش جامع Logging در پایتون؛ خداحافظی با print

🐍 HomeOfPython
|
📅 1404/10/19

سطح مقدماتی (Beginner Level)

بسیاری از برنامه‌نویسان تازه کار برای پیدا کردن خطاها یا بررسی روند اجرای برنامه از دستور print() استفاده می‌کنند. اما در پروژه‌های واقعی و محیط‌های عملیاتی (Production)، استفاده از print یک اشتباه بزرگ است. پایتون ماژول قدرتمندی به نام logging دارد که امکانات بسیار بیشتری را در اختیار ما قرار می‌دهد.

چرا Print نه؟

۱. عدم دسته‌بندی: شما نمی‌توانید پیام‌ها را بر اساس اهمیت (مثلاً خطا، هشدار یا صرفاً اطلاع‌رسانی) جدا کنید. ۲. کند بودن: دستور print می‌تواند عملکرد برنامه را در حجم بالا کند کند. ۳. مدیریت سخت: تغییر محل ذخیره پیام‌ها (مثلاً از کنسول به فایل) با print دشوار است.

سطوح لاگ (Log Levels)

در پایتون ۵ سطح اصلی برای لاگ کردن وجود دارد که به ترتیب اهمیت عبارتند از:

  1. DEBUG: جزئیات دقیق، معمولاً فقط برای عیب‌یابی.
  2. INFO: تایید اینکه کارها طبق انتظار پیش می‌روند.
  3. WARNING: نشان‌دهنده یک اتفاق غیرمنتظره، اما برنامه هنوز کار می‌کند (پیش‌فرض).
  4. ERROR: یک مشکل جدی که باعث شده بخشی از برنامه کار نکند.
  5. CRITICAL: خطای بسیار مهلک که ممکن است کل برنامه را متوقف کند.

استفاده اولیه (Basic Config)

برای شروع سریع، می‌توانیم از basicConfig استفاده کنیم.

مثال ۱: رفتار پیش‌فرض

به طور پیش‌فرض، پایتون فقط سطح WARNING و بالاتر را نمایش می‌دهد.

Python

مثال ۲: تغییر سطح لاگ

می‌توانیم سطح حساسیت را تغییر دهیم تا پیام‌های INFO یا DEBUG هم نمایش داده شوند.

Python

مثال ۳: ذخیره در فایل (File Logging)

به جای نمایش در کنسول، می‌توانیم خروجی را مستقیماً به یک فایل بفرستیم.

python
import logging

# تمام لاگ‌ها در فایل app.log ذخیره می‌شوند
logging.basicConfig(
    filename='app.log',
    filemode='w', # 'w' برای بازنویسی، 'a' برای ادامه نوشتن (append)
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

logging.info("This message goes to the file, not console.")

سطح پیشرفته (Professional Level)

در برنامه‌های بزرگ و حرفه‌ای، استفاده از basicConfig روی root logger (لاگر اصلی) توصیه نمی‌شود. در عوض، باید لاگرهای اختصاصی، هندلرها (Handlers) و فرمترهای (Formatters) شخصی‌سازی شده بسازیم.

مفهوم Logger، Handler و Formatter

  • Logger: آبجکتی که در کد صدا می‌زنید (رابط اصلی).
  • Handler: تعیین می‌کند لاگ به کجا برود (کنسول، فایل، ایمیل، دیتابیس).
  • Formatter: تعیین می‌کند لاگ با چه ظاهری ثبت شود.

ساخت لاگر اختصاصی (Custom Logger)

بهترین روش این است که برای هر ماژول یک لاگر جداگانه داشته باشیم. معمولاً از نام فایل جاری __name__ به عنوان نام لاگر استفاده می‌شود.

مثال ۴: معماری حرفه‌ای لاگینگ

در این مثال یک لاگر می‌سازیم که هم‌زمان لاگ‌ها را در کنسول (با رنگ یا فرمت ساده) و در فایل (با جزئیات کامل) ذخیره کند.

Python

لاگ کردن خطاها با Traceback

یکی از مهم‌ترین کاربردهای لاگینگ، ثبت کامل متن خطا (Exception) است بدون اینکه برنامه کرش کند. برای این کار از پارامتر exc_info=True یا متد logger.exception استفاده می‌کنیم.

مثال ۵: ثبت کامل استثناها

Python

چرخش فایل‌های لاگ (Rotating File Logs)

اگر برنامه شما ماه‌ها اجرا شود، فایل لاگ ممکن است گیگابایتی شود. برای جلوگیری از این مشکل، از RotatingFileHandler استفاده می‌کنیم که پس از رسیدن به حجم خاص، فایل جدیدی می‌سازد.

مثال ۶: محدود کردن حجم فایل لاگ

python
import logging
from logging.handlers import RotatingFileHandler

logger = logging.getLogger("RotatingApp")
logger.setLevel(logging.INFO)

# این هندلر وقتی فایل به 5 بایت (خیلی کم برای تست) برسد، فایل جدید می‌سازد
# و حداکثر 2 فایل پشتیبان نگه می‌دارد.
handler = RotatingFileHandler('app.log', maxBytes=2000, backupCount=2)

logger.addHandler(handler)

# شبیه‌سازی پر کردن فایل لاگ
for i in range(100):
    logger.info(f"This is log line number {i} - filling up the file...")

نکات کلیدی عملکرد (Performance Tips)

۱. Lazy Evaluation: از f-string در لاگ‌هایی که ممکن است اجرا نشوند (مثل debug در محیط production) با احتیاط استفاده کنید. روش استاندارد logging بهتر است زیرا اگر سطح لاگ پایین باشد، هزینه ساخت رشته را نمی‌پردازید.

python
# بد (رشته همیشه ساخته می‌شود حتی اگر لاگ نشود)
logging.debug(f"User {user.get_heavy_data()} logged in")

# خوب (تابع فقط زمانی اجرا می‌شود که سطح لاگ اجازه دهد)
logging.debug("User %s logged in", user_name)