آموزش logging در جنگو
سخت ترین بخش حل یک مشکل، فهمیدن آن است. در پروژه های بزرگ، دیباگ کردن و پیدا کردن دلیل یک خطا میتواند کار بسیار دشواری باشد. بسیاری از برنامه نویسان از دستور print برای دیباگ پروژه استفاده میکنند و این عیبی ندارد. اما این فقط برای پروژه های کوچک مناسب است و در پروژه های بزرگ دستور print فقط به درد عمه تان میخورد.
ویدیو پیشنهادی: آموزش ماژول logging پایتون
# logging چیست؟
ابزار logging یک راه حل انعطاف پذیر برای سرعت بخشیدن به دیباگ است. شما با تنظیم logging میتوانید هر اتفاقی که در پروژه رخ می دهد را ثبت کنید تا در زمان مشکل بتوانید از آنها استفاده کنید.
فریمورک جنگو یک ابزار کامل logging در خود دارد که میتواند پیام های log را در سطح های مختلف دریافت کرده و به شکل های مختلفی ذخیره کند. جنگو از ماژول Logging پایتون استفاده و امکاناتی را به این ماژول اضافه کرده است.
دوره پیشنهادی: آموزش پیشرفته ساخت فروشگاه اینترنتی با جنگو
# ارسال درخواست log
برای ارسال یک پیام log باید در کدتان از ماژول logging استفاده کنید. ابتدا ماژول logging پایتون را ایمپورت کرده و سپس با استفاده از یک logging.getLogger لاگر جدید ایجاد کنید. سپس باید به این لاگر یک اسم هم اختصاص دهید تا مشخص شود که چه کسی پیغام لاگ را نوشته است. برای این کار میتوانید از __name__ استفاده کنید که مسیر کامل ماژول پایتون را مشخص میکند:
import logging
logger = logging.getLogger(__name__)
بهتر است ایجاد کردن logger را در سطح ماژول انجام دهید. و بعد در یک فانکشن، مثلا یک view، میتوانید یک پیغام را به logger ارسال کنید.
def some_view(request):
...
if some_risky_state:
logger.warning("Platform is running at risk")
وقتی این کد اجرا شود، یک log حاوی پیغامی که ارسال کرده بودید ثبت خواهد شد. اگر از تنظیمات پیشفرض جنگو استفاده کنید، این پیغام در خروجی کنسول نمایش داده خواهد شد.
شما میتوانید سطح اهمیت پیغام خود را نیز مشخص کنید. در کد بالا از سطح warning استفاده کردیم، به این معنی که این یک پیغام هشدار است. میتوانید از دیگر سطوح پیغام هم استفاده کنید. مثل DEBUG, INFO, WARNING, ERROR, CRITICAL. یک مثال دیگر:
logger.critical("Payment system is not responding")
دقت کنید که پیغام هایی با سطح پایینتر از Warning در کنسول نمایش داده نمی شوند. تغییر این رفتار نیازمند کانفیگ اضافی است.
ویدیو پیشنهادی: آموزش if name == main در پایتون
# تغییر تنظیمات logging جنگو
شما میتوانید تنظیمات پیشفرض logging جنگو را تغییر دهید و مشخص کنید که پیغام های log به چه شکی و در کجا ذخیره شوند. شما میتوانید موارد زیر را تنظیم کنید:
- ✔️ با logger مشخص میکنید که هر پیغام log به کدام handler ارسال شود.
- ✔️ با handler مشخص میکنید که هر پیغام log در کجا ذخیره شود.
- ✔️ با filter مشخص میکنید که پیغام های log با چه سطح اهمیتی ذخیره شوند.
- ✔️ با formatter مشخص میکنید که هر پیغام log با چه فرمتی ذخیره شود.
روش های زیادی برای تنظیم logging وجود دارد. مهمترین روش استفاده از LOGGING در فایل settings.py است.
+ تنظیمات پایه logging جنگو
اگر میخواهید logging را برای پروژه جنگو خود تنظیم کنید، بهتر است کارهای زیر را انجام دهید
اضافه کردن دیکشنری LOGGING به تنظیمات
در فایل settings.py پروژه جنگو دیکشنری زیر را اضافه کنید:
LOGGING = {
"version": 1, # the dictConfig format version
"disable_existing_loggers": False, # retain the default loggers
}
مقدار version نسخه لاگ را مشخص میکند و یک گزینه اجباری مربوط به پایتون است و میتوانید همیشه مقدار آن را برابر 1 قرار دهید. مقدار disable_existing_loggers فعال بودن logger پیشفرض جنگو را مشخص میکند که بهتر است این مقدار را False قرار دهید تا logger پیشفرض جنگو بدون تغییر بماند.
ویدیو پیشنهادی: آموزش فایل در پایتون
تنظیم یک handler
این تنظیمات یک handler به نام file را مشخص میکند که از FileHandler پایتون برای ذخیره پیام های لاگ با سطح اهمیت DEBUG و بالاتر، در یک فایل به نام general.log استفاده میکند.
LOGGING = {
# ...
"handlers": {
"file": {
"class": "logging.FileHandler",
"filename": "general.log",
"level": "DEBUG",
},
},
}
تنظیم یک logger
برای ارسال پیام به handler که بالا تنظیم کردیم، باید یک logger نیز مشخص کنید
LOGGING = {
# ...
"loggers": {
"": {
"level": "DEBUG",
"handlers": ["file"],
},
},
}
نام لاگر مشخص میکند که کدام پیغام ها پردازش خواهند شد. در اینجا لاگر نام ندارد ('') به این معنی که تمام پیغام ها از تمام logger ها را دریافت خواهد کرد. این لاگر پیغام های با سطح اهمیت DEBUG و بالاتر را به file ارسال خواهد کرد و این handler هم پیغام ها را در فایل ذخیره میکند.
دقت کنید که یک logger میتوانید هر پیغام را به چند handler ارسال کند، پس رابطه بین logger ها و handler ها چند به چند است.
اگر کد زیر را اجرا کنید:
logger.debug("Attempting to connect to API")
این پیغام در فایل general.log ذخیره خواهد شد.
تنظیم یک formatter
با استفاده از formatter میتوانید اطلاعات بیشتری را در کنار پیام log ذخیره کنید. برای هر formatter باید یک نام انتخاب کنید. کد زیر دو formatter با نام های verbose و simple ایجاد میکند.
LOGGING = {
# ...
"formatters": {
"verbose": {
"format": "{name} {levelname} {asctime} {module} {process:d} {thread:d} {message}",
"style": "{",
},
"simple": {
"format": "{levelname} {message}",
"style": "{",
},
},
}
کلید style برای مشخص کردن { برای str.format و $ برای string.Template است. پیشفرض $ است.
برای اضافه کردن یک formatter به handler باید آن را در دیکشنری handler مشخص کنید و به نام formatter اشاره کنید:
"handlers": {
"file": {
"class": "logging.FileHandler",
"filename": "general.log",
"formatter": "verbose",
},
}
استفاده از فضای نام logger
آن logger بدون نامی که ایجاد کردیم، تمام پیغام ها را از هر برنامه پایتونی دریافت خواهد کرد اما لاگری که نام داشته باشد، فقط پیغام هایی را دریافت میکند که به آنها ارسال شده باشد.
فضای نام هر logger در تابع getLogger مشخص خواهد شد. اگر کد زیر در فایل views.py در اپ my_app اجرا شود:
logger = logging.getLogger(__name__)
یک logger با فضای نام my_app.views ایجاد خواهد کرد. استفاده از __name__ باعث می شود که پیغام ها را براساس مبدا به شکل خودکار سازماندهی کنید. همچنین از تداخل نام ها نیز جلوگیری می کند.
یک logger با نام my_app.views تمام پیغام ها از این مسیر را دریافت خواهد کرد:
LOGGING = {
# ...
"loggers": {
"my_app.views": {...},
},
}
همچنین میتوانید فضای نام logger را به شکل صریح مشخص کنید:
logger = logging.getLogger("project.payment")
+ سلسله مراتب logging جنگو
نام گذاری logger سلسله مراتبی است. مثلا my_app والد my_app.views است و خود my_app.views نیز والد my_app.views.private حساب می شود. تمام پیغام ها از فرزند به والد به ارث می رسند. پیغامی که در my_app.views.private پردازش میشود، در my_app و my_app.views نیز پردازش خواهند شد مگر اینکه این رفتار را تغییر دهید.
برای تنظیم این رفتار میتوانید از کلید propagate استفاده کنید.
LOGGING = {
# ...
"loggers": {
"my_app": {
# ...
},
"my_app.views": {
# ...
},
"my_app.views.private": {
# ...
"propagate": False,
},
},
}
مقدار propagate به شکل پیشفرض برابر با True است. در مثال بالا، ما سلسله مراتب را غیرفعال کردیم و پیغام هایی که وارد my_app.views.private میشوند دیگر به سطح بالاتر نمی روند. اما پیغامی که در my_app.views پردازش می شود به سطح بالاتر خواهد رفت زیرا برای این سطح مقدار propagate هنوز True است.
# نتیجه گیری
برنامه نویسان پایتون از دستور print به عنوان یک روش ساده و سریع برای دیباگ استفاده میکنند. استفاده از logging فقط کمی سختر از دستور print است اما بسیار حرفه ای تر و قدرتمندتر است. تنظیم logging در پروژه جنگو علاوه بر اشکال زدایی، می تواند اطلاعات بیشتر و تمیزتری درباره وضعیت و سلامت پروژه در اختیار شما قرار دهد.