Logging در پایتون — آموزش لاگ کردن یا ثبت وقایع

۲۱۷۵ بازدید
آخرین به‌روزرسانی: ۲۱ دی ۱۴۰۱
زمان مطالعه: ۱۸ دقیقه
Logging در پایتون — آموزش لاگ کردن یا ثبت وقایع

از آنجا که «پایتون» (Python) یکی از زبان‌های برنامه نویسی همه‌منظوره و بسیار محبوب به حساب می‌آید، دارای انواع ابزارهای سودمند برای پیشبرد برنامه نویسی است. «لاگ کردن»، ثبت وقایع یا همان Logging در پایتون از روش‌های بسیار کاربردی پایتون به شمار می‌رود که ابزاری با همین نام نیز در زبان پایتون وجود دارد. این ابزار می‌تواند به برنامه نویسان کمک کند تا درک بهتری از برنامه نویسی داشته باشند و به وسیله آن سناریوهایی ارائه می‌شوند که ممکن است برنامه نویس در حین توسعه تا به حال به آن‌ها فکر نکرده باشد. در این مطلب سعی شده به طور جامع به بررسی Logging در پایتون و آموزش ثبت وقایع به همراه ابزارهای آن‌ها پرداخته شود.

997696

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‌های پیش فرض و بدون نیاز به برنامه نویسی و تنظیمات زیادی، وقایع مورد نظر را در برنامه‌ها ثبت کنند. متدهای متناظر با هر کدام از موارد فوق به صورت زیر در برنامه‌ها استفاده می‌شوند:

1import logging
2
3logging.debug('This is a debug message')
4logging.info('This is an info message')
5logging.warning('This is a warning message')
6logging.error('This is an error message')
7logging.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  ارائه شده است:

1import logging
2
3logging.basicConfig(level=logging.DEBUG)
4logging.debug('This will get logged')

خروجی کدهای فوق به صورت زیر نمایش داده می‌شود:

DEBUG:root:This will get logged

حال پس از این قطعه کد، همه رویدادها در سطح اهمیت DEBUG و بالاتر از آن لاگ یا ثبت خواهند شد. به صورت مشابه، برای Logging یک فایل به جای یک کنسول، می‌توان از پارامترهای filename  و filemode  استفاده کرد و همچنین می‌توان فرمت پیام خود را با استفاده از پارامتر format  مشخص کرد. کدهای زیر این مثال توصیف شده را نشان می‌دهند:

1import logging
2
3logging.basicConfig(filename='app.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s')
4logging.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 فرایند به همراه سطح و پیام وجود داشته باشد، باید کدهایی شبیه به کدهای زیر نوشته شوند:

1import logging
2
3logging.basicConfig(format='%(process)d-%(levelname)s-%(message)s')
4logging.warning('This is a Warning')

خروجی کدهای فوق به صورت زیر نمایش داده شده‌اند:

18472-WARNING-This is a Warning

در کدهای فوق، format  می‌تواند رشته‌ای با «ویژگی‌های» (Attributeهای) LogRecord  را با هر ترتیبی بگیرد که برنامه نویس در نظر دارد. در ادامه مثالی درباره اضافه کردن فرمت زمان و تاریخ به برنامه نمایش داده شده است:

1import logging
2
3logging.basicConfig(format='%(asctime)s - %(message)s', level=logging.INFO)
4logging.info('Admin logged in')

پس از پیاده‌سازی، خروجی کدهای فوق به صورت زیر نمایش داده می‌شود:

2018-07-11 20:12:06,288 - Admin logged in

دستور %(asctime)s  زمان ایجاد LogRecord  را در برنامه اضافه می‌کند. فرمت می‌تواند با استفاده از ویژگی datefmt  تغییر کند. این ویژگی از زبان فرمت یکسانی با فرمت توابع در ماژول datetime  مانند time.strftime()  استفاده می‌کند. مثالی برای این فرمت در ادامه ارائه شده است:

1import logging
2
3logging.basicConfig(format='%(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S')
4logging.warning('Admin logged out')

خروجی کدهای فوق به صورت زیر نمایش داده شده است:

12-Jul-18 20:53:19 - Admin logged out

فیلم های آموزش برنامه نویسی پایتون

معرفی فیلم های آموزش برنامه نویسی پایتون (Python)

دوره‌های آموزش ویدیویی در پلتفرم فرادرس بر اساس موضوع به صورت مجموعه‌های آموزشی مختلفی دسته‌بندی می‌شوند. یکی از این مجموعه‌ها برای آموزش‌های زبان برنامه نویسی پایتون از سطح مقدماتی تا پیشرفته همراه با انواع روش‌های کاربردی آن از جمله لاگ کردن در پایتون است. با توجه به این‌که این زبان برنامه نویسی در حوزه‌های مختلف علوم کامپیوتر مورد استفاده قرار می‌گیرد، این بخش به معرفی برخی از این آموزش‌ها برای یادگیری بیش‌تر پایتون در کاربردهای مختلف اختصاص داده شده است. در زمان تدوین این مطلب، مجموعه دوره‌های برنامه نویسی فرادرس حاوی بیش از ۳۲۲ ساعت محتوای ویدیویی است و شامل ۶۰ دوره می‌شود. در ادامه برخی از دوره‌های این مجموعه آموزشی به طور خلاصه معرفی شده‌اند:

  • فیلم آموزش برنامه نویسی پایتون Python - مقدماتی (طول مدت: ۱۹ ساعت و ۵۳ دقیقه، مدرس: پژمان اقبالی شمس آبادی): در این فرادرس زبان برنامه نویسی پایتون از پایه‌‌ای‌ترین مباحث تدریس می‌شود و برای دانشجویان و علاقه‌مندان به پایتون مناسب است. برای مشاهده فیلم آموزش برنامه نویسی پایتون Python - مقدماتی + کلیک کنید.
  • فیلم آموزش ترفندهای برنامه نویسی پایتون (طول مدت: 9 ساعت و 9 دقیقه، مدرس: دکتر سید مصطفی کلامی هریس): در این دوره آموزشی فرادرس، سعی می‌شود مهم‌ترین و کاربردی‌ترین نکات استفاده از زبان برنامه‌نویسی پایتون و کتابخانه‌های استاندارد آن، مورد بررسی قرار گیرد. با آموختن نکات و ترفندهایی که در این آموزش ارائه شده‌ است، دانش برنامه‌نویسی فرد به سطح بالاتر از متوسط می‌رسد و می‌تواند برای آموختن نکات پیشرفته‌تر، برنامه‌ریزی کند. برای مشاهده فیلم آموزش ترفندهای برنامه نویسی پایتون + کلیک کنید.
  • فیلم آموزش برنامه نویسی شی گرا در پایتون Python (طول مدت: ۷ ساعت و ۲۹ دقیقه، مدرس: دکتر فرشید شیرافکن): در این دوره آموزشی مفاهیم شی گرایی در پایتون با ساده‌ترین روش و همراه با ذکر مثال آموزش داده شده است. برای مشاهده فیلم آموزش برنامه نویسی شی گرا در پایتون Python + کلیک کنید.
  • فیلم آموزش کتابخانه های NumPy و Matplotlib در پایتون (طول مدت: ۴ ساعت و ۴۶ دقیقه، مدرس: میترا تجربه کار): این دوره آموزشی برای تکمیل مباحث موجود در دوره پایتون مقدماتی ارائه شده است. همچنین آشنایی با کتابخانه NumPy، بخش جدیدی از برنامه نویسی پایتون را در این دوره به دانشجویان و علاقه‌مندان معرفی می‌کند. برای مشاهده فیلم آموزش کتابخانه های NumPy و Matplotlib در پایتون + کلیک کنید.
  • فیلم آموزش پایتون گرافیکی - رابط های گرافیکی پایتون (طول مدت: ۵ ساعت و ۳ دقیقه، مدرس: سید رضا دهقان): برای برنامه نویسان پایتون، یادگیری حداقل یک واسط گرافیکی (Graphical User Interface | GUI) این زبان برنامه نویسی اهمیت بسیاری دارد. به همین دلیل، در این دوره آموزشی به بررسی واسط‌های گرافیکی پایتون پرداخته شده است. برای مشاهده فیلم آموزش پایتون گرافیکی - رابط‌های گرافیکی پایتون + کلیک کنید.
  • فیلم آموزش پروژه محور Python پایتون - ساخت نرم افزار برای ویندوز و لینوکس در Python (طول مدت: ۹ ساعت و ۳۴ دقیقه، مدرس: محمد حسینی): ابزار توسعه در این فرادرس بر مبنای PyQt است. با استفاده از این فرادرس، علاقه‌مندان با نحوه تولید نرم افزار آشنا می‌شوند و می‌توانند در هر زمینه‌ای نرم افزاری مورد نیاز خود را بسازند. برای مشاهده فیلم آموزش پروژه محور Python پایتون - ساخت نرم افزار برای ویندوز و لینوکس در Python + کلیک کنید.

داده های متغیر در Logging پایتون

در بیشتر موارد، برنامه نویسان قصد دارند اطلاعات پویا یا دینامیک را از اپلیکیشن خود در لاگ‌ها ذخیره کنند. متدهای Logging از رشته‌ها به عنوان آرگومان خود استفاده می‌کنند و ممکن است که یک رشته با داده‌های متغیر در خط‌های مجزا فرمت‌دهی و سپس به متد Log ارسال شود. اما این کار می‌تواند به طور مستقیم با استفاده از فرمت‌دهی رشته برای پیام و اضافه کردن داده‌های متغیر به عنوان آرگومان‌ها انجام شود. در ادامه مثالی برای درک بهتر این بخش ارائه شده است:

1import logging
2
3name = 'John'
4
5logging.error('%s raised an error', name)

پس از پیاده‌سازی کدهای فوق، خروجی زیر نمایش داده می‌شود:

ERROR:root:John raised an error

آرگومان‌هایی که وارد متد می‌شوند، نیاز است که شامل داده‌های متغیر در پیام‌ها باشند. برای این رویکرد از هر نوع سبک فرمت‌دهی می‌توان استفاده کرد، با این حال «f-strings» که در نسخه پایتون ۳.۶ منتشر شد، بهترین روش برای فرمت‌دهی رشته‌ها است؛ زیرا می‌تواند به کوتاه نگه داشتن فرمت‌دهی و خواندن آسان آن‌ها کمک کند. در ادامه مثالی برای درک این موضوع ارائه شده است:

1import logging
2
3name = 'John'
4
5logging.error(f'{name} raised an error')

خروجی کدهای فوق در ادامه ارائه شده است:

ERROR:root:John raised an error
لاگ کردن در برنامه نویسی

ردیابی پشته ای در ثبت وقایع چیست؟

با استفاده از ماژول Logging در پایتون می‌توان ردیابی یا همان Trace را در برنامه‌های پایتون انجام داد. اگر پارامتر exc_info  به عنوان True  در نظر گرفته شود، می‌توان «اطلاعات استثنا» (Exception information) را دریافت کرد و توابع Logging به صورت زیر فراخوانی می‌شوند:

1import logging
2
3a = 5
4b = 0
5
6try:
7  c = a / b
8except Exception as e:
9  logging.error("Exception occurred", exc_info=True)

خروجی برنامه فوق پس از پیاده‌سازی به صورت زیر نمایش داده می‌شود:

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]

اگر پارامتر exc_info  به عنوان True  تنظیم نشود، خروجی برنامه فوق، چیزی درباره استثنا نشان نخواهد داد، یا به عبارت دیگر در یک سناریو دنیای واقعی می‌توان گفت که ممکن است به سادگی ZeroDivisionError  نباشد. در این حالت تصور می‌شود که خطایی در یک «پایگاه کد» (Codebase) پیچیده با یک Log اشکال‌زدایی شده است و فقط خروجی زیر را نشان می‌دهد:

ERROR:root:Exception occurred

اگر Logging کردن با یک کنترل کننده استثنا انجام شود، از متد logging.exception()  استفاده می‌شود که پیامی را با سطح ERROR ثبت و اطلاعات استثنا را به پیام اضافه کند. به عبارت دیگر، فراخوانی logging.exception()  مانند فراخوانی logging.error(exc_info=True)  است. اما از آن‌جایی که این روش همیشه اطلاعات استثنا را حذف می‌کند، فقط باید از یک کنترل کننده استثنا فراخوانی شود. برای درک بهتر این موضوع مثال زیر ارائه شده است:

1import logging
2
3a = 5
4b = 0
5try:
6  c = a / b
7except Exception as e:
8  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 به هر بخش مورد نیاز نجات می‌دهد. در ادامه مثالی برای درک بهتر این موضوع ارائه شده است:

1import logging
2
3logger = logging.getLogger('example_logger')
4logger.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 و بالاتر از آن نیز باید در یک فایل ذخیره شوند. برای درک بهتر این موضوع در ادامه مثالی ارائه شده است:

1# logging_example.py
2
3import logging
4
5# Create a custom logger
6logger = logging.getLogger(__name__)
7
8# Create handlers
9c_handler = logging.StreamHandler()
10f_handler = logging.FileHandler('file.log')
11c_handler.setLevel(logging.WARNING)
12f_handler.setLevel(logging.ERROR)
13
14# Create formatters and add it to handlers
15c_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
16f_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
17c_handler.setFormatter(c_format)
18f_handler.setFormatter(f_format)
19
20# Add handlers to the logger
21logger.addHandler(c_handler)
22logger.addHandler(f_handler)
23
24logger.warning('This is a warning')
25logger.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  مطابقت پیدا می‌کند. در ادامه کدهای مرتبط با این بخش ارائه شده‌اند:

1# run.py
2
3import logging_example

خروجی کدهای فوق، پس از پیاده‌سازی به صورت زیر نمایش داده می‌شود:

logging_example - WARNING - This is a warning
logging_example - ERROR - This is an error
لاگینگ چگونه انجام می شود

متد های دیگر تنظیم Logging در پایتون

همان‌طور که در بخش پیشین مورد بررسی قرار گرفت، با استفاده از ماژول، کلاس و توابع یا با ایجاد پیکربندی و تنظیمات فایل یا دیکشنری و بارگذاری به ترتیب آن با fileConfig()  یا dictConfig()  می‌توان لاگ گردن در پایتون را انجام داد. این روش‌ها در صورتی که هدف تغییر تنظیمات Logging در پایتون باشد، می‌توانند برای اپلیکیشن‌ها و برنامه‌ها بسیار مفید و کاربردی باشند. در ادامه مثالی از تنظیمات و پیکربندی فایل ارائه شده است:

1[loggers]
2keys=root,sampleLogger
3
4[handlers]
5keys=consoleHandler
6
7[formatters]
8keys=sampleFormatter
9
10[logger_root]
11level=DEBUG
12handlers=consoleHandler
13
14[logger_sampleLogger]
15level=DEBUG
16handlers=consoleHandler
17qualname=sampleLogger
18propagate=0
19
20[handler_consoleHandler]
21class=StreamHandler
22level=DEBUG
23formatter=sampleFormatter
24args=(sys.stdout,)
25
26[formatter_sampleFormatter]
27format=%(asctime)s - %(name)s - %(levelname)s - %(message)s

در کدهای فوق، دو Logger، یک Handler و یک Formatter وجود دارند. بعد از این‌که نام آن‌ها تعریف شده، با اضافه کردن کلمات logger  ، handler  و formatter  قبل از نام آن‌ها همراه با یک «علامت خط زیرین» (Underline)، پیکربندی می‌شوند. برای بارگذاری این فایل تنظیمات، باید از فایل fileConfig()  به صورت زیر استفاده شود:

1import logging
2import logging.config
3
4logging.config.fileConfig(fname='file.conf', disable_existing_loggers=False)
5
6# Get the logger specified in the file
7logger = logging.getLogger(__name__)
8
9logger.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 برای رویکرد دیکشنری ارائه شده است:

1version: 1
2formatters:
3  simple:
4    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
5handlers:
6  console:
7    class: logging.StreamHandler
8    level: DEBUG
9    formatter: simple
10    stream: ext://sys.stdout
11loggers:
12  sampleLogger:
13    level: DEBUG
14    handlers: [console]
15    propagate: no
16root:
17  level: DEBUG
18  handlers: [console]

در ادامه مثالی برای نشان دادن بارگذاری کانفیگ یا تنظیمات از فایل YAML ارائه شده است:

1import logging
2import logging.config
3import yaml
4
5with open('config.yaml', 'r') as f:
6    config = yaml.safe_load(f.read())
7    logging.config.dictConfig(config)
8
9logger = logging.getLogger(__name__)
10
11logger.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)، خروجی استانداری را لاگ می‌کند؛ زیرا این خروجی می‌تواند مستقیماً و به راحتی توسط خود کانتینر مدیریت شود. در ادامه بخشی از کدهای این مثال ارائه شده‌اند:

1import logging
2import os
3logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO"))
4exit(main())

با استفاده از کدهای فوق، اپلیکیشن می‌تواند پیام‌هایی را با سطح اهمیت اطلاعات یا بالاتر از آن را در stderr  ثبت کند. در خروجی از فرمت ساده‌ای به صورت زیر استفاده شده است:

ERROR:the.module.name:The log message

حتی می‌توان اپلیکیشن را طوری پیکربندی و تنظیم کرد که شامل پیام‌های خطایابی یا شاید فقط خطا باشد. این کار با تنظیم متغیر محیطی LOGLEVEL انجام می‌شود. با این حال مشکلی که ممکن است با این روش به وجود بیاید، استثناهایی هستند که به عنوان چندین خط لاگ می‌شوند و امکان دارد که مشکلاتی را برای تجزیه و تحلیل‌های بعدی به وجود بیاورد. با این حال تنظیمات و پیکربندی پایتون برای ارسال چندین خط از استثناها به صورت یک خط معمولاً کار دشواری به حساب می‌آید؛ ولی ممکن است امکان‌پذیر باشد. باید به این موضوع توجه داشت که در کدهای ارائه شده زیر، فراخوانی logging.exception  معادل logging.error  است و در این کدها exc_infoTrue  در نظر گرفته می‌شود.

1import logging
2import os
3 
4class OneLineExceptionFormatter(logging.Formatter):
5    def formatException(self, exc_info):
6        result = super().formatException(exc_info)
7        return repr(result)
8 
9    def format(self, record):
10        result = super().format(record)
11        if record.exc_text:
12            result = result.replace("n", "")
13        return result
14 
15handler = logging.StreamHandler()
16formatter = OneLineExceptionFormatter(logging.BASIC_FORMAT)
17handler.setFormatter(formatter)
18root = logging.getLogger()
19root.setLevel(os.environ.get("LOGLEVEL", "INFO"))
20root.addHandler(handler)
21 
22try:
23    exit(main())
24except Exception:
25    logging.exception("Exception in main(): ")
26    exit(1)

مثال دوم استفاده ماژول Logging در پایتون

در این مثال درباره Syslog برنامه‌ای ایجاد خواهد شد و Syslog گزینه جایگزین مثال قبل، ارسال مستقیم آن به Syslog است. این روش برای سیستم‌عامل‌های قدیمی‌تر کاربرد بیشتری دارد که Systemd ندارد.

در یک دنیای ایده‌آل برنامه نویسی، بهتر است همه چیز ساده باشد. اما متأسفانه، زبان برنامه نویسی پایتون به پیکربندی و تنظیمات دقیق‌تری نیاز دارد تا بتواند پیام‌های لاگ «یونیکد» (Unicode) ارسال کند. در ادامه کدهای این مثال ارائه شده‌اند:

1import logging
2import logging.handlers
3import os
4 
5class SyslogBOMFormatter(logging.Formatter):
6    def format(self, record):
7        result = super().format(record)
8        return "ufeff" + result
9 
10handler = logging.handlers.SysLogHandler('/dev/log')
11formatter = SyslogBOMFormatter(logging.BASIC_FORMAT)
12handler.setFormatter(formatter)
13root = logging.getLogger()
14root.setLevel(os.environ.get("LOGLEVEL", "INFO"))
15root.addHandler(handler)
16 
17try:
18    exit(main())
19except Exception:
20    logging.exception("Exception in main()")
21    exit(1)

جمع‌بندی

ماژول Logging در پایتون، ماژولی بسیار انعطاف‌پذیر به حساب می‌آید. این ماژول برای لاگ کردن (ثبت وقایع) در پایتون بسیار کاربردی است و هر لاگ مورد نظری را می‌توان بر اساس سطح اهمیت آن بررسی کرد و نشان داد. می‌توان Logging‌های پایه و ساده‌ای را به پروژه‌های کوچک اضافه یا با استفاده از ابزارها و روش‌های موجود، متُدهای لاگ کردن را در سطح‌های مختلفی به صورت سفارشی ایجاد کرد. برخی از ابزارها برای ایجاد این لاگ‌ها شامل کلاس‌های Handler ،Formatter و سایر موارد می‌شوند. معمولاً از روش‌های سفارشی برای کار بر روی پروژه‌های بزرگ استفاده شده است.

ثبت وقایع چیست

در این مطلب سعی شد به طور جامع به همه این موارد پرداخته شود و انواع مثال‌های کاربردی برای آن ارائه شده است. اگر برنامه نویسی در برنامه‌های خود از ویژگی لاک کردن در پایتون استفاده نمی‌کند، پیشنهاد می‌شود که با وجود مزایای بسیار Logging در پایتون شروع به استفاده از آن کند. هنگامی که لاگ کردن و ثبت وقایع در پایتون به درستی انجام شود، مطمئناً اصطحکاک زیادی را از روند توسعه برنامه حذف خواهد کرد و به برنامه‌نویسان و توسعه‌دهندگان کمک می‌کند تا فرصت‌هایی خوبی پیدا کنند و سطح برنامه‌های خود را بالاتر ببرند. همچنین در این مطلب سعی شد برخی از دوره‌های آموزشی ویدویی فرادرس به دانشجویان و علاقه‌مندان برای یادگیری بیشتر معرفی شود.

بر اساس رای ۱۶ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
ًReal PythonGeeksforGeeksSENTRYsolarwinds loggly
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *