Logging در پایتون — آموزش لاگ کردن یا ثبت وقایع
از آنجا که «پایتون» (Python) یکی از زبانهای برنامه نویسی همهمنظوره و بسیار محبوب به حساب میآید، دارای انواع ابزارهای سودمند برای پیشبرد برنامه نویسی است. «لاگ کردن»، ثبت وقایع یا همان Logging در پایتون از روشهای بسیار کاربردی پایتون به شمار میرود که ابزاری با همین نام نیز در زبان پایتون وجود دارد. این ابزار میتواند به برنامه نویسان کمک کند تا درک بهتری از برنامه نویسی داشته باشند و به وسیله آن سناریوهایی ارائه میشوند که ممکن است برنامه نویس در حین توسعه تا به حال به آنها فکر نکرده باشد. در این مطلب سعی شده به طور جامع به بررسی Logging در پایتون و آموزش ثبت وقایع به همراه ابزارهای آنها پرداخته شود.
Logging در پایتون چیست ؟
لاگها (Log) این امکان را برای توسعه دهندگان به وجود میآورند که به طور دائم جریانی که در یک برنامه در حال وقوع است را با دقت مضاعف، مانند چشمانی اضافه دنبال کنند. میتوان گفت که لاگها به معنی گزارشها در هر لحظه هستند. آنها میتوانند اطلاعاتی مانند دسترسی هر IP یا اپلیکیشن به برنامه را ذخیره کنند. اگر خطایی در برنامه رخ دهد، لاگ کردن میتواند به صورت واضحتری نسبت به «ردیابی پشتهای» (Stack Trace) وضعیت برنامه را مشخص کند.
با Logging در برنامه پایتون به صورت کارآمد و در بخش مناسب برنامه، نه تنها میتوان خطاهای برنامه را راحتتر بررسی کرد، بلکه میتوان از دادهها برای تجزیه و تحلیل عملکرد برنامه به منظور برنامهریزی برای مقیاسبندی یا نگاهی به الگوهای استفاده برای برنامهریزی و بازاریابی استفاده کرد. پایتون سیستم Logging، لاگ کردن یا ثبت وقایع را به عنوان بخشی از کتابخانههای استاندارد خود در نظر گرفته است. بنابراین میتوان به راحتی Logging را به اپلیکیشن یا برنامه مورد نظر خود اضافه کرد.
ماژول Logging در پایتون چیست ؟
ماژول Logging در پایتون، یک ماژول بسیار قدرتمند است که هم برای رفع نیازهای برنامه نویسان تازهکار و هم برای تیمهای حرفهای و سازمانی برنامه نویسی پایتون کاربرد دارد. این ماژول با اکثر کتابخانههای «وابسته» (Third Party) پایتون استفاده میشود. بنابراین، میتوان پیام لاگهای برنامه را با پیامهای کتابخانه ادغام کرد تا بتوان لاگهای همگنی برای اپلیکیشن خود به وجود آورد. با استفاده از دستور import
یا همان «وارد کردن» به صورت زیر میتوان این کتابخانه را در برنامه بارگذاری کرد.
import logging
پس از وارد شدن ماژول Logging در برنامه پایتون، با استفاده از فراخوانی کلمه logger
میتوان پیامهای مورد نظر خود را ثبت کرد. به صورت پیشفرض، ۵ سطح استاندارد برای رویدادهای برنامه وجود دارند که دشواری آنها را نشان میدهند. هر کدام از آنها یک «متُد» (Method) مختص به خود دارد که میتواند برای ثبت وقایع در آن سطح مورد استفاده قرار بگیرد. در ادامه، سطحهای وقایع بر اساس افزایش سطح اهمیت آنها به ترتیب از کم به زیاد فهرست شدهاند:
- «DEBUG» (اشکالزدایی): مقدار ارزش عددی این مورد دارای سطح اهمیت ۱۰ است.
- «INFO» (اطلاعات): دارای مقدار ارزش عددی اهمیتی برابر با ۲۰ است.
- «WARNING» (اخطار): این سطح دارای مقدار ارزشی برابر با ۳۰ است.
- «ERROR» (خطا): مقدار ارزش این سطح اهمیت، ۴۰ است.
- «CRITICAL» (بحران): مقدار ارزش این سطح اهمیت به عنوان پر اهمیتترین سطح برابر با ۵۰ است.
ماژول Logging در پایتون امکانی را برای برنامه نویسان به وجود میآورد که با استفاده از Loggerهای پیش فرض و بدون نیاز به برنامه نویسی و تنظیمات زیادی، وقایع مورد نظر را در برنامهها ثبت کنند. متدهای متناظر با هر کدام از موارد فوق به صورت زیر در برنامهها استفاده میشوند:
import logging
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')
خروجی کدهای فوق به صورت زیر پس از پیادهسازی نشان داده میشوند:
WARNING:root:This is a warning message ERROR:root:This is an error message CRITICAL:root:This is a critical message
خروجی فوق سطح اهمیت را قبل از هر پیام همراه با root
نشان داده است. root
نامی است که ماژول Logging در پایتون به logger
پیش فرض خود میدهد. این ماژول در خروجی خود سطح، نام و پیام را به صورت جداگانه و با علامت «دو نقطه» (Colon) مشخص کرده است. همچنین، فرمت خروجی پیشفرض ماژول Logging در پایتون را به گونهای میتوان تنظیم کرد که شامل مواردی از جمله «مهر زمانی» (Timestamp)، «شماره خط» (Line Number) و جزئیات دیگر باشد.
باید به این نکته توجه داشت که پیامهای متدهای debug()
و info()
در کدهای فوق ثبت نشدهاند؛ این موضوع به این دلیل است که به صورت پیش فرض ماژول Logging در پایتون، فقط پیام سطحهای WARNING و بالاتر از آن را لاگ میکند. اما میتوان در صورت نیاز تنظیمات پیش فرض این ماژول را با ورودی به سیستم برای ثبت رویدادهای همه سطوح تغییر داد. همچنین میتوان سطحهای اهمیت مورد نظر خود را در تنظیمات تعریف کرد، اما معمولاً این کار توصیه نمیشود؛ زیرا، میتواند باعث سردرگمی برخی از کتابخانههای وابسته در حال استفاده شود.
ماژول Logging در پایتون دارای ویژگیهای بسیاری است. میتوان گفت که این ماژول «ثابتها» (Constant)، کلاسها و متدهای بسیاری دارد. در ادامه مطلب «Logging در پایتون» برخی از این ویژگیهای ماژول Logging در پایتون فهرست شدهاند. در این بخش مواردی که با حروف بزرگ نوشته میشوند، کلاسها هستند و مواردی که با حروف کوچک شروع شدهاند، نشاندهنده متدها در این ماژول هستند:
- Logger.info(msg)
: این ویژگی پیامی با سطح اطلاعات را در Logger پایتون ثبت میکند.
- Logger.warning(msg)
: این متد دارای پیامی در سطح اخطار است.
- Logger.error(msg)
: از این متد در سطح خطا استفاده میشود.
- Logger.critical(msg)
: این ویژگی پیامی را در سطح بحرانی در Logger پایتون ثبت میکند.
- Logger.log(lvl,msg)
: این ویژگی پیامی را در سطح عدد «صحیح» (Integer) « lvl
» در Logger پایتون ثبت میکند.
- Logger.exception(msg)
: این ویژگی پیامی را در سطح خطا ثبت کرده است.
- Logger.setLevel(lvl)
: این تابع آستانه Logger را در lvl
تنظیم کرده است. به عبارت دیگر به این معنی است که همه پیامهای زیر این سطح را نادیده میگیرد.
- Logger.addFilter(filt)
: با استفاده از این ویژگی، یک فیلتر خاص filt
به Logger در پایتون اضافه میشود.
- Logger.removeFilter(filt)
: این ویژگی، یک فیلتر منحصر به فرد filt
را از Logger پایتون حذف میکند.
- Logger.filter(record)
: روش این ویژگی، فیلتر Logger را روی رکورد ارائه شده اعمال میکند و اگر رکورد پردازش شود، True و در غیر این صورت False را برمیگرداند.
- Logger.addHandler(hdlr)
: این روش یک کلاس handler
منحصر به فرد hdlr
را به Logger پیاتون اضافه میکند.
- Logger.removeHandler(hdlr)
: این روش یک کلاس handler
منحصر به فرد hdlr
را از Logger پایتون حذف میکند.
- Logger.hasHandlers()
: این ویژگی بررسی میکند که آیا Logger توسط کلاس handler
تنظیم و پیکربندی شده یا این اتفاق رخ نداده است.
تنظیمات اولیه لاگ کردن در پایتون چگونه است؟
میتوان برای تنظیم ماژول Logging در پایتون به حالت مورد نظر خود از متدی با کلمه کلیدی basicConfig
استفاده کرد. برخی از پارامترهای رایج مورد استفاده برای متد basicConfig()
در ادامه نمایش داده شدهاند:
- level
: با استفاده از این پارامتر root logger
روی سطح اهمیت خاص و مورد نظر خودش تنظیم میشود.
- filename
: این پارامتر نام فایل مشخص میشود.
- filemode
: اگر filename
داده شود، فایل در این حالت باز میشود. مقدار پیش فرض این پارامتر a
است که معنی «اضافه کردن» (Append) میدهد.
- format
: این پارامتر فرمت پیام لاگ را مشخص میکند.
با استفاده از پارامتر level
، میتوان تعیین کرد که چه سطحی از پیامهای لاگ وجود داشته باشند. این موضوع میتواند با وارد کردن متغیر ثابت به کلاس انجام شود و این کار باعث خواهد شد که همه لاگهای فراخوانی شده در آن سطح یا بالاتر از آن قرار گیرند. در ادامه مثالی برای استفاده از پارامتر level
ارائه شده است:
import logging
logging.basicConfig(level=logging.DEBUG)
logging.debug('This will get logged')
خروجی کدهای فوق به صورت زیر نمایش داده میشود:
DEBUG:root:This will get logged
حال پس از این قطعه کد، همه رویدادها در سطح اهمیت DEBUG و بالاتر از آن لاگ یا ثبت خواهند شد. به صورت مشابه، برای Logging یک فایل به جای یک کنسول، میتوان از پارامترهای filename
و filemode
استفاده کرد و همچنین میتوان فرمت پیام خود را با استفاده از پارامتر format
مشخص کرد. کدهای زیر این مثال توصیف شده را نشان میدهند:
import logging
logging.basicConfig(filename='app.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s')
logging.warning('This will get logged to a file')
پس از پیادهسازی کدهای فوق، در خروجی، عبارت زیر نمایش داده میشود:
root - ERROR - This will get logged to a file
در خروجی کدهای فوق، به جای کنسول اصلی، نام فایل app.log
نوشته میشود. حالت فایل با حرف w
تنظیم شده که به معنی این است که هر بار basicConfig()
فراخوانی میشود، فایل لاگ در «حالت نوشتن» (Write Mode) باز خواهد شد و هر بار اجرای برنامه فایل را بازنویسی میکند. مقدار پیشفرض پارامتر حالت فایل a
است که Append را نشان میدهد. همچنین، میتوان سفارشی سازیهای بیشتری را نیز با استفاده از تابع basicConfig()
انجام داد. باید به این موضوع نیز توجه شود که فراخوانی basicConfig()
برای تنظیم root logger
، تنها در صورتی کار میکند که root logger
قبلاً تنظیم نشده باشد. اصولاً این تابع فقط یک بار فراخوانی میشود.
همچنین، متدهای debug()
، info()
، warning()
، error()
و critical()
در صورتی که قبلاً فراخوانی نشده باشند، تابع basicConfig()
را بدون آرگومان و به طور خودکار فراخوانی میکنند. به عبارت دیگر، پس از بار اولی که توابع فوق فراخوانی میشود، دیگر نمیتوان root logger
را برای آنها تنظیم کرد؛ زیرا آنها تابع basicConfig()
را یک بار به صورت داخلی فراخوانی کردهاند. تنظیمات پیش فرض برای تابع basicConfig()
به این صورت است که Logger به گونهای تنظیم خواهد شد که در خروجی عبارت زیر نوشته شود:
ERROR:root:This is an error message
فرمت خروجی ماژول Logging در پایتون چیست ؟
در حالی که میتوان هر متغیری را که نشان دهنده «رشتهای» (String) از برنامه است به عنوان یک پیام Log در ماژول Logging در پایتون ارسال کرد، برخی از عناصر پایه وجود دارند که خودشان بخشی از LogRecord
هستند و میتوانند به راحتی به فرمت خروجی اضافه شوند. اگر قصد لاگ کردن ID فرایند به همراه سطح و پیام وجود داشته باشد، باید کدهایی شبیه به کدهای زیر نوشته شوند:
import logging
logging.basicConfig(format='%(process)d-%(levelname)s-%(message)s')
logging.warning('This is a Warning')
خروجی کدهای فوق به صورت زیر نمایش داده شدهاند:
18472-WARNING-This is a Warning
در کدهای فوق، format
میتواند رشتهای با «ویژگیهای» (Attributeهای) LogRecord
را با هر ترتیبی بگیرد که برنامه نویس در نظر دارد. در ادامه مثالی درباره اضافه کردن فرمت زمان و تاریخ به برنامه نمایش داده شده است:
import logging
logging.basicConfig(format='%(asctime)s - %(message)s', level=logging.INFO)
logging.info('Admin logged in')
پس از پیادهسازی، خروجی کدهای فوق به صورت زیر نمایش داده میشود:
2018-07-11 20:12:06,288 - Admin logged in
دستور %(asctime)s
زمان ایجاد LogRecord
را در برنامه اضافه میکند. فرمت میتواند با استفاده از ویژگی datefmt
تغییر کند. این ویژگی از زبان فرمت یکسانی با فرمت توابع در ماژول datetime
مانند time.strftime()
استفاده میکند. مثالی برای این فرمت در ادامه ارائه شده است:
import logging
logging.basicConfig(format='%(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S')
logging.warning('Admin logged out')
خروجی کدهای فوق به صورت زیر نمایش داده شده است:
12-Jul-18 20:53:19 - Admin logged out
فیلم های آموزش برنامه نویسی پایتون
دورههای آموزش ویدیویی در پلتفرم تم آف بر اساس موضوع به صورت مجموعههای آموزشی مختلفی دستهبندی میشوند. یکی از این مجموعهها برای آموزشهای زبان برنامه نویسی پایتون از سطح مقدماتی تا پیشرفته همراه با انواع روشهای کاربردی آن از جمله لاگ کردن در پایتون است. با توجه به اینکه این زبان برنامه نویسی در حوزههای مختلف علوم کامپیوتر مورد استفاده قرار میگیرد، این بخش به معرفی برخی از این آموزشها برای یادگیری بیشتر پایتون در کاربردهای مختلف اختصاص داده شده است. در زمان تدوین این مطلب، مجموعه دورههای برنامه نویسی تم آف حاوی بیش از ۳۲۲ ساعت محتوای ویدیویی است و شامل ۶۰ دوره میشود. در ادامه برخی از دورههای این مجموعه آموزشی به طور خلاصه معرفی شدهاند:
- فیلم آموزش برنامه نویسی پایتون Python – مقدماتی (طول مدت: ۱۹ ساعت و ۵۳ دقیقه، مدرس: پژمان اقبالی شمس آبادی): در این تم آف زبان برنامه نویسی پایتون از پایهایترین مباحث تدریس میشود و برای دانشجویان و علاقهمندان به پایتون مناسب است. برای مشاهده فیلم آموزش برنامه نویسی پایتون Python – مقدماتی + کلیک کنید.
- فیلم آموزش ترفندهای برنامه نویسی پایتون (طول مدت: 9 ساعت و 9 دقیقه، مدرس: دکتر سید مصطفی کلامی هریس): در این دوره آموزشی تم آف، سعی میشود مهمترین و کاربردیترین نکات استفاده از زبان برنامهنویسی پایتون و کتابخانههای استاندارد آن، مورد بررسی قرار گیرد. با آموختن نکات و ترفندهایی که در این آموزش ارائه شده است، دانش برنامهنویسی فرد به سطح بالاتر از متوسط میرسد و میتواند برای آموختن نکات پیشرفتهتر، برنامهریزی کند. برای مشاهده فیلم آموزش ترفندهای برنامه نویسی پایتون + کلیک کنید.
- فیلم آموزش برنامه نویسی شی گرا در پایتون Python (طول مدت: ۷ ساعت و ۲۹ دقیقه، مدرس: دکتر فرشید شیرافکن): در این دوره آموزشی مفاهیم شی گرایی در پایتون با سادهترین روش و همراه با ذکر مثال آموزش داده شده است. برای مشاهده فیلم آموزش برنامه نویسی شی گرا در پایتون Python + کلیک کنید.
- فیلم آموزش کتابخانه های NumPy و Matplotlib در پایتون (طول مدت: ۴ ساعت و ۴۶ دقیقه، مدرس: میترا تجربه کار): این دوره آموزشی برای تکمیل مباحث موجود در دوره پایتون مقدماتی ارائه شده است. همچنین آشنایی با کتابخانه NumPy، بخش جدیدی از برنامه نویسی پایتون را در این دوره به دانشجویان و علاقهمندان معرفی میکند. برای مشاهده فیلم آموزش کتابخانه های NumPy و Matplotlib در پایتون + کلیک کنید.
- فیلم آموزش پایتون گرافیکی – رابط های گرافیکی پایتون (طول مدت: ۵ ساعت و ۳ دقیقه، مدرس: سید رضا دهقان): برای برنامه نویسان پایتون، یادگیری حداقل یک واسط گرافیکی (Graphical User Interface | GUI) این زبان برنامه نویسی اهمیت بسیاری دارد. به همین دلیل، در این دوره آموزشی به بررسی واسطهای گرافیکی پایتون پرداخته شده است. برای مشاهده فیلم آموزش پایتون گرافیکی – رابطهای گرافیکی پایتون + کلیک کنید.
- فیلم آموزش پروژه محور Python پایتون – ساخت نرم افزار برای ویندوز و لینوکس در Python (طول مدت: ۹ ساعت و ۳۴ دقیقه، مدرس: محمد حسینی): ابزار توسعه در این تم آف بر مبنای PyQt است. با استفاده از این تم آف، علاقهمندان با نحوه تولید نرم افزار آشنا میشوند و میتوانند در هر زمینهای نرم افزاری مورد نیاز خود را بسازند. برای مشاهده فیلم آموزش پروژه محور Python پایتون – ساخت نرم افزار برای ویندوز و لینوکس در Python + کلیک کنید.
داده های متغیر در Logging پایتون
در بیشتر موارد، برنامه نویسان قصد دارند اطلاعات پویا یا دینامیک را از اپلیکیشن خود در لاگها ذخیره کنند. متدهای Logging از رشتهها به عنوان آرگومان خود استفاده میکنند و ممکن است که یک رشته با دادههای متغیر در خطهای مجزا فرمتدهی و سپس به متد Log ارسال شود. اما این کار میتواند به طور مستقیم با استفاده از فرمتدهی رشته برای پیام و اضافه کردن دادههای متغیر به عنوان آرگومانها انجام شود. در ادامه مثالی برای درک بهتر این بخش ارائه شده است:
import logging
name = 'John'
logging.error('%s raised an error', name)
پس از پیادهسازی کدهای فوق، خروجی زیر نمایش داده میشود:
ERROR:root:John raised an error
آرگومانهایی که وارد متد میشوند، نیاز است که شامل دادههای متغیر در پیامها باشند. برای این رویکرد از هر نوع سبک فرمتدهی میتوان استفاده کرد، با این حال «f-strings» که در نسخه پایتون ۳.۶ منتشر شد، بهترین روش برای فرمتدهی رشتهها است؛ زیرا میتواند به کوتاه نگه داشتن فرمتدهی و خواندن آسان آنها کمک کند. در ادامه مثالی برای درک این موضوع ارائه شده است:
import logging
name = 'John'
logging.error(f'{name} raised an error')
خروجی کدهای فوق در ادامه ارائه شده است:
ERROR:root:John raised an error
ردیابی پشته ای در ثبت وقایع چیست؟
با استفاده از ماژول Logging در پایتون میتوان ردیابی یا همان Trace را در برنامههای پایتون انجام داد. اگر پارامتر exc_info
به عنوان True
در نظر گرفته شود، میتوان «اطلاعات استثنا» (Exception information) را دریافت کرد و توابع Logging به صورت زیر فراخوانی میشوند:
import logging
a = 5
b = 0
try:
c = a / b
except Exception as e:
logging.error("Exception occurred", exc_info=True)
خروجی برنامه فوق پس از پیادهسازی به صورت زیر نمایش داده میشود:
ERROR:root:Exception occurred Traceback (most recent call last): File "exceptions.py", line 6, inc = a / b ZeroDivisionError: division by zero [Finished in 0.2s]
اگر پارامتر exc_info
به عنوان True
تنظیم نشود، خروجی برنامه فوق، چیزی درباره استثنا نشان نخواهد داد، یا به عبارت دیگر در یک سناریو دنیای واقعی میتوان گفت که ممکن است به سادگی ZeroDivisionError
نباشد. در این حالت تصور میشود که خطایی در یک «پایگاه کد» (Codebase) پیچیده با یک Log اشکالزدایی شده است و فقط خروجی زیر را نشان میدهد:
ERROR:root:Exception occurred
اگر Logging کردن با یک کنترل کننده استثنا انجام شود، از متد logging.exception()
استفاده میشود که پیامی را با سطح ERROR ثبت و اطلاعات استثنا را به پیام اضافه کند. به عبارت دیگر، فراخوانی logging.exception()
مانند فراخوانی logging.error(exc_info=True)
است. اما از آنجایی که این روش همیشه اطلاعات استثنا را حذف میکند، فقط باید از یک کنترل کننده استثنا فراخوانی شود. برای درک بهتر این موضوع مثال زیر ارائه شده است:
import logging
a = 5
b = 0
try:
c = a / b
except Exception as e:
logging.exception("Exception occurred")
خروجی کدهای فوق به صورت زیر نمایش داده میشوند:
ERROR:root:Exception occurred Traceback (most recent call last): File "exceptions.py", line 6, in c = a / b ZeroDivisionError: division by zero [Finished in 0.2s]
استفاده از متد logging.exception()
لاگها را در سطح ERROR نشان میدهد. اگر برنامه نویسی نخواهد که این اتفاق رخ دهد، میتواند هر متد دیگری را از debug()
و critical()
انتخاب کند و پارامتر exc_info
را به عنوان True
در نظر بگیرد.
کلاس ها و توابع در ماژول Logging در پایتون
تاکنون، یک لاگر پیشفرض به نام root
بررسی شده است که توسط ماژول Logging در پایتون هر زمان استفاده میشود که توابع آن مستقیماً به صورت logging.debug()
فراخوانی شوند. میتوان با استفاده از ایجاد شیئی از کلاس Logger
، Logger خود را تعریف کرد، مخصوصاً این مورد زمانی کاربرد دارد که اپلیکیشن دارای چندین ماژول باشد. در ادامه برخی از کلاسهای رایج در ماژول Logging در پایتون فهرست شدهاند:
- Logger
: این کلاسی است که اشیا آن به صورت مستقیم در کدهای اپلیکیشن استفاده میشوند تا توابع فراخوانی شوند.
- LogRecord
: Loggerها به صورت خودکار اشیا LogRecord
را تولید میکنند که دارای همه اطلاعاتی است که به رویدادهای لاگ از جمله نام Logger، تابع، شماره خط، پیام و سایر موارد ارتباط دارند.
- Handler
: این کلاسها، کلاسهای LogRecord
را به مقصد خروجی مورد نیاز مانند کنسول یا یک فایل ارسال میکنند. کلاس Handler
پایهای برای «زیرکلاسهایی» (Subclass) از جمله StreamHandler
، FileHandler
، SMTPHandler
، HTTPHandler
و سایر موارد است. این زیرکلاسها خروجیهای Logging در پایتون را به مقصد مورد نظرشان مانند sys.stdout
یا فایل دیسک ارسال میکنند.
- Formatter
: با استفاده از این کلاس با تعیین فرمت رشتهای که حاوی فهرستی از ویژگیهای خروجی است، فرمت خروجی مشخص میشود.
در این رویکرد، برنامه نویسان بیشتر با اشیاء کلاس Logger در پایتون سر و کار دارند که با استفاده از تابع سطح ماژول logging.getLogger(name)
نمونه سازی میشوند. فراخوانیهای متعدد به getLogger()
با همان name
، مرجعی را به همان شی Logger برمیگرداند که برنامه نویس را از ارسال اشیاء Logger به هر بخش مورد نیاز نجات میدهد. در ادامه مثالی برای درک بهتر این موضوع ارائه شده است:
import logging
logger = logging.getLogger('example_logger')
logger.warning('This is a warning')
در ادامه کدهای فوق پیادهسازی شدهاند و خروجی آنها نمایش داده شده است:
This is a warning
مثال فوق، یک Logger سفارشی با نام example_logger
را ایجاد میکند، اما برخلاف Root Logger، نام Logger سفارشی ساخته شده توسط برنامه نویسان بخشی از فرمت خروجی پیش فرض نیست و باید به تنظیمات اضافه شود. تنظیم آن به فرمتی که در خروجی نام Logger را نشان دهد، خروجی مانند دستورات زیر را نشان خواهد داد:
WARNING:example_logger:This is a warning
برخلاف Root Logger، یک Logger سفارشی را نمیتوان با استفاده از تابع basicConfig()
تنظیم کرد. این Loggerها با استفاده از کلاسهای «Handler» و «Formatter» تنظیم میشوند.
استفاده از کلاس Handler برای تنظیم Logger سفارشی چگونه است؟
از کلاس Handler زمانی استفاده میشود که برنامه نویسی قصد تنظیم Loggerهای سفارشی و فرستادن لاگها به مکانهای مختلف پس از تولید آنها را داشته باشد. این کلاس، پیامهای لاگ را به مقصدهای تنظیم شده از جمله جریان خروجی استاندارد یا یک فایل را از طریق HTTP یا به ایمیل افراد از طریق SMTP ارسال میکند. Loggerی که برنامه نویسان به صورت سفارشی ایجاد کردهاند، میتواند بیش از یک Handler داشته باشد. به عبارت دیگر، میتوان آن را طوری تنظیم کرد که در یک فایل Log ذخیره و از طریق ایمیل هم ارسال شود.
مانند Logger در پایتون میتوان در کلاس Handler نیز سطح اهمیت برای پیامهای لاگ تعیین کرد. این قابلیت زمانی بسیار کاربردی و مفید است که برنامه نویس قصد داشته باشد چندین Handler را برای Logger یکسانی تنظیم کند و هر کدام دارای سطح اهمیت متفاوتی باشند. برای مثال، توسعه دهنده قصد دارد لاگهایی با سطح WARNING و بالاتر از آن در کنسول ثبت کند، اما همه موارد دارای سطح ERROR و بالاتر از آن نیز باید در یک فایل ذخیره شوند. برای درک بهتر این موضوع در ادامه مثالی ارائه شده است:
# logging_example.py
import logging
# Create a custom logger
logger = logging.getLogger(__name__)
# Create handlers
c_handler = logging.StreamHandler()
f_handler = logging.FileHandler('file.log')
c_handler.setLevel(logging.WARNING)
f_handler.setLevel(logging.ERROR)
# Create formatters and add it to handlers
c_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
f_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
c_handler.setFormatter(c_format)
f_handler.setFormatter(f_format)
# Add handlers to the logger
logger.addHandler(c_handler)
logger.addHandler(f_handler)
logger.warning('This is a warning')
logger.error('This is an error')
در ادامه خروجی کدهای فوق ارائه شده است:
__main__ - WARNING - This is a warning __main__ - ERROR - This is an error
در کدهای فوق، logger.warning()
با استفاده از LogRecord
ایجاد میشود، این Logger همه اطلاعات را درباره رویدادها نگه میدارد و آن را به همه Handlerهایی که دارد از جمله کلاس c_handler
و f_handler
ارسال میکند. در این مثال کلاس c_handler
یک StreamHandler
با سطح WARNING است که اطلاعاتی را از LogRecord
برای ایجاد و تولید خروجیهایی در فرمتهای مشخص شده دریافت میکند و آنها را در کنسول چاپ میکند.
کلاس f_handler
یک FileHandler
با سطح ERROR به حساب میآید و LogRecord
را به این دلیل نادیده میگیرد که سطح آن WARNING است. زمانی که logger.error()
فراخوانی میشود، کلاس c_handler
دقیقاً مانند قبل رفتار خواهد کرد و کلاس f_handler
یک LogRecord
در سطح ERROR دریافت میکند. بنابراین، مانند کلاس c_handler
به تولید خروجی اقدام کرده، اما به جای چاپ پیام در کنسول، آن را در فایل مشخص شده به صورت زیر مینویسد:
2018-08-03 16:12:21,723 - __main__ - ERROR - This is an error
نام Logger مربوط به متغیر __name__
به صورت __main__
ثبت میشود، این نامی است که پایتون به ماژولی اختصاص میدهد که در آن اجرا شروع شده بود. اگر این فایل با برخی از ماژولهای دیگر Import شود، سپس، متغیر __name__
با نام logging_example
مطابقت پیدا میکند. در ادامه کدهای مرتبط با این بخش ارائه شدهاند:
# run.py
import logging_example
خروجی کدهای فوق، پس از پیادهسازی به صورت زیر نمایش داده میشود:
logging_example - WARNING - This is a warning logging_example - ERROR - This is an error
متد های دیگر تنظیم Logging در پایتون
همانطور که در بخش پیشین مورد بررسی قرار گرفت، با استفاده از ماژول، کلاس و توابع یا با ایجاد پیکربندی و تنظیمات فایل یا دیکشنری و بارگذاری به ترتیب آن با fileConfig()
یا dictConfig()
میتوان لاگ گردن در پایتون را انجام داد. این روشها در صورتی که هدف تغییر تنظیمات Logging در پایتون باشد، میتوانند برای اپلیکیشنها و برنامهها بسیار مفید و کاربردی باشند. در ادامه مثالی از تنظیمات و پیکربندی فایل ارائه شده است:
[loggers]
keys=root,sampleLogger
[handlers]
keys=consoleHandler
[formatters]
keys=sampleFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler
[logger_sampleLogger]
level=DEBUG
handlers=consoleHandler
qualname=sampleLogger
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=sampleFormatter
args=(sys.stdout,)
[formatter_sampleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
در کدهای فوق، دو Logger، یک Handler و یک Formatter وجود دارند. بعد از اینکه نام آنها تعریف شده، با اضافه کردن کلمات logger
، handler
و formatter
قبل از نام آنها همراه با یک «علامت خط زیرین» (Underline)، پیکربندی میشوند. برای بارگذاری این فایل تنظیمات، باید از فایل fileConfig()
به صورت زیر استفاده شود:
import logging
import logging.config
logging.config.fileConfig(fname='file.conf', disable_existing_loggers=False)
# Get the logger specified in the file
logger = logging.getLogger(__name__)
logger.debug('This is a debug message')
پس از پیادهسازی کدهای فوق، خروجی زیر نمایش داده شده است:
2018-07-13 13:57:45,467 - __main__ - DEBUG - This is a debug message
مسیر فایل کانفیگ (پیکربندی) و تنظیم به عنوان پارامتر به متد fileConfig()
داده میشود و پارامتر disable_existing_loggers
برای نگه داشتن یا غیرفعال کردن لاگرهای موجود در هنگام فراخوانی تابع استفاده میشود. اگر True و False در این کدها ذکر نشده باشد، به صورت پیش فرض روی True تنظیم میشود. در ادامه کدهایی از تنظیمات مشابهی از فرمت YAML برای رویکرد دیکشنری ارائه شده است:
version: 1
formatters:
simple:
format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
console:
class: logging.StreamHandler
level: DEBUG
formatter: simple
stream: ext://sys.stdout
loggers:
sampleLogger:
level: DEBUG
handlers: [console]
propagate: no
root:
level: DEBUG
handlers: [console]
در ادامه مثالی برای نشان دادن بارگذاری کانفیگ یا تنظیمات از فایل YAML ارائه شده است:
import logging
import logging.config
import yaml
with open('config.yaml', 'r') as f:
config = yaml.safe_load(f.read())
logging.config.dictConfig(config)
logger = logging.getLogger(__name__)
logger.debug('This is a debug message')
پس از پیادهسازی کدهای فوق، خروجی آنها به صورت زیر نمایش داده میشود:
2018-07-13 14:05:03,766 - __main__ - DEBUG - This is a debug message
چرا Print کردن روش خوبی برای Logging در پایتون نیست؟
برخی از توسعه دهندگان از مفهوم «چاپ کردن» (Printing) «عبارتها» (Statement) استفاده میکنند تا بررسی و تأیید کنند که دستورات و عبارتها به درستی انجام میشوند یا مشکلی وجود دارد. اما روش چاپ کردن عبارتها ایده خوبی نیست. این روش فقط مشکلات را برای اسکریپتها و پروژههای کوچک برطرف میکند و برای برنامههای بزرگ و پیچیده با شکست مواجه خواهد شد.
بنابراین استفاده کردن از ماژول استاندارد و داخلی Logging در پایتون که پیامهایی را برای وضعیت برنامه در بخشهای مختلف ارائه میدهد، روش بهتری به منظور بررسی و ثبت وقایع در برنامه است. همانطور که در این مطلب بررسی شد، فایل Logging در پایتون میتواند شامل اطلاعاتی باشد که هر بخش از کدها پس از پیادهسازی بررسی شوند و نشان دهند که ممکن است دارای چه مشکلاتی باشند. در بخش بعدی از مطلب «Logging در پایتون» به بررسی و شرح مزایا و معایب روش ثبت وقایع یا همان لاگ کردن در پایتون میپردازیم.
مزایا و معایت روش Logging در پایتون چیست؟
حال که با قوانین و موارد استفاده پایهای و روشهای مختلف Logging در پایتون آشنا شدیم، بهتر است با برخی از مزایا و معایب این شیوه ثبت وقایع در پایتون نیز آشنا شد. از جمله مزایایی که برای روش Logging در پایتون میتوان به آنها پرداخت این است که وقتی از ماژول Logging در پایتون استفاده میشود، بهتر است لاگهای دریافتی در اپلیکیشن نگهداری شوند. همچنین میتوان لاگها و وقایع مورد نیاز خود را به صورت سفارشی تنظیم کرد. با این حال، این مزایای ذکر شده، مانند هر ماژول و ویژگی دیگری، بدون عیب و ایراد نخواهند بود.
برای مثال، زمانی که وقایعی در سطح اخطار یا همان WARNING یا هر سطحی بالاتر از آن تنظیم میشوند، اگر فایلهای دائمی در تمام اجرای برنامه حفظ شوند، فایلهای لاگ به سرعت از نظر «اندازه» (Size) رشد میکنند. بنابراین، استفاده از Logging در پایتون برای اشکالزدایی (Debugging) دارای چالشهای اینچنینی خواهد بود. علاوه بر این، بررسی لاگهای خطا کار دشورای است، مخصوصاً زمانی که پیامهای خطای ثبت وقایع نمیتوانند محتوای کافی را تولید کنند. برای مثال، زمانی که از ویژگی logging.error(message)
بدون تنظیم کردن (ست کردن) متُد exc_info
به حالت True
استفاده میشود، بسیار دشوار است که دلیل ریشهای این مشکل با پیامهای غیر کامل بررسی شود.
در حالی که لاگ کردن در پایتون فقط اطلاعات تشخیص را در مورد چیزی ارائه میدهد که باید در اپلیکیشن اصلاح شود، ابزارهای نظارتی مختلفی وجود دارند که میتوانند اطلاعات دقیقتری ارائه دهند تا برنامهها و اپلیکیشنها را بتوان به راحتی اشکالزدایی، اصلاح و مشکلات عملکردی آنها را برطرف کرد. به عنوان یکی از این ابزارهای نظارتی میتوان به «Sentry» اشاره داشت. در ادامه مطلب «Logging در پایتون» پس از بررسی انواع جنبههای مختلف لاگ کردن و ثبت وقایع در پایتون، به بررسی مثالهایی از این روش و ماژول Logging در اپلیکیشنهای پایتون پرداخته شده است.
مثال اول استفاده ماژول Logging در پایتون
در این مثال، هدف لاگ کردن خروجی استاندارد برای Systemd است که با استفاده از ماژول Logging در پایتون انجام خواهد شد. استفاده از ماژول Logging در پایتون یکی از سادهترین و بهترین روشها برای لاگ کردن برنامه به حساب میآید. هنگام استفاده از Systemd برای اجرای Daemon یا برنامه، اپلیکیشنها فقط میتوانند پیامهای لاگ کردن را به متدهای stdout
یا stderr
ارسال کنند و Systemd پیامها را به journald
و syslog
میفرستند. به عنوان یک امتیاز اضافی، این روش نیازی به گرفتن استثنا ندارد؛ زیرا پایتون قبل از این، آنها را با خطاهای استاندارد نوشته است.
پس باید از قراردادهای موجود برای حل این مثال پیروی و استثناها مدیریت شوند. در این مثال، پیادهسازی پایتون در «کانتینرهایی» (Container) از جمله «داکر» (Docker)، خروجی استانداری را لاگ میکند؛ زیرا این خروجی میتواند مستقیماً و به راحتی توسط خود کانتینر مدیریت شود. در ادامه بخشی از کدهای این مثال ارائه شدهاند:
import logging
import os
logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO"))
exit(main())
با استفاده از کدهای فوق، اپلیکیشن میتواند پیامهایی را با سطح اهمیت اطلاعات یا بالاتر از آن را در stderr
ثبت کند. در خروجی از فرمت سادهای به صورت زیر استفاده شده است:
ERROR:the.module.name:The log message
حتی میتوان اپلیکیشن را طوری پیکربندی و تنظیم کرد که شامل پیامهای خطایابی یا شاید فقط خطا باشد. این کار با تنظیم متغیر محیطی LOGLEVEL انجام میشود. با این حال مشکلی که ممکن است با این روش به وجود بیاید، استثناهایی هستند که به عنوان چندین خط لاگ میشوند و امکان دارد که مشکلاتی را برای تجزیه و تحلیلهای بعدی به وجود بیاورد. با این حال تنظیمات و پیکربندی پایتون برای ارسال چندین خط از استثناها به صورت یک خط معمولاً کار دشواری به حساب میآید؛ ولی ممکن است امکانپذیر باشد. باید به این موضوع توجه داشت که در کدهای ارائه شده زیر، فراخوانی logging.exception
معادل logging.error
است و در این کدها exc_info
True
در نظر گرفته میشود.
import logging
import os
class OneLineExceptionFormatter(logging.Formatter):
def formatException(self, exc_info):
result = super().formatException(exc_info)
return repr(result)
def format(self, record):
result = super().format(record)
if record.exc_text:
result = result.replace("n", "")
return result
handler = logging.StreamHandler()
formatter = OneLineExceptionFormatter(logging.BASIC_FORMAT)
handler.setFormatter(formatter)
root = logging.getLogger()
root.setLevel(os.environ.get("LOGLEVEL", "INFO"))
root.addHandler(handler)
try:
exit(main())
except Exception:
logging.exception("Exception in main(): ")
exit(1)
مثال دوم استفاده ماژول Logging در پایتون
در این مثال درباره Syslog برنامهای ایجاد خواهد شد و Syslog گزینه جایگزین مثال قبل، ارسال مستقیم آن به Syslog است. این روش برای سیستمعاملهای قدیمیتر کاربرد بیشتری دارد که Systemd ندارد.
در یک دنیای ایدهآل برنامه نویسی، بهتر است همه چیز ساده باشد. اما متأسفانه، زبان برنامه نویسی پایتون به پیکربندی و تنظیمات دقیقتری نیاز دارد تا بتواند پیامهای لاگ «یونیکد» (Unicode) ارسال کند. در ادامه کدهای این مثال ارائه شدهاند:
import logging
import logging.handlers
import os
class SyslogBOMFormatter(logging.Formatter):
def format(self, record):
result = super().format(record)
return "ufeff" + result
handler = logging.handlers.SysLogHandler('/dev/log')
formatter = SyslogBOMFormatter(logging.BASIC_FORMAT)
handler.setFormatter(formatter)
root = logging.getLogger()
root.setLevel(os.environ.get("LOGLEVEL", "INFO"))
root.addHandler(handler)
try:
exit(main())
except Exception:
logging.exception("Exception in main()")
exit(1)
جمعبندی
ماژول Logging در پایتون، ماژولی بسیار انعطافپذیر به حساب میآید. این ماژول برای لاگ کردن (ثبت وقایع) در پایتون بسیار کاربردی است و هر لاگ مورد نظری را میتوان بر اساس سطح اهمیت آن بررسی کرد و نشان داد. میتوان Loggingهای پایه و سادهای را به پروژههای کوچک اضافه یا با استفاده از ابزارها و روشهای موجود، متُدهای لاگ کردن را در سطحهای مختلفی به صورت سفارشی ایجاد کرد. برخی از ابزارها برای ایجاد این لاگها شامل کلاسهای Handler ،Formatter و سایر موارد میشوند. معمولاً از روشهای سفارشی برای کار بر روی پروژههای بزرگ استفاده شده است.
در این مطلب سعی شد به طور جامع به همه این موارد پرداخته شود و انواع مثالهای کاربردی برای آن ارائه شده است. اگر برنامه نویسی در برنامههای خود از ویژگی لاک کردن در پایتون استفاده نمیکند، پیشنهاد میشود که با وجود مزایای بسیار Logging در پایتون شروع به استفاده از آن کند. هنگامی که لاگ کردن و ثبت وقایع در پایتون به درستی انجام شود، مطمئناً اصطحکاک زیادی را از روند توسعه برنامه حذف خواهد کرد و به برنامهنویسان و توسعهدهندگان کمک میکند تا فرصتهایی خوبی پیدا کنند و سطح برنامههای خود را بالاتر ببرند. همچنین در این مطلب سعی شد برخی از دورههای آموزشی ویدویی تم آف به دانشجویان و علاقهمندان برای یادگیری بیشتر معرفی شود.