logging.config --- پیکربندی گزارشگیری¶
کد منبع: Lib/logging/config.py
این بخش، API برای پیکربندی ماژول logging را شرح میدهد.
توابع پیکربندی¶
توابع زیر ماژول logging را پیکربندی میکنند. آنها در ماژول logging.config قرار دارند. استفاده از آنها اختیاری است --- شما میتوانید ماژول logging را با استفاده از این توابع یا با فراخوانی API اصلی (تعریفشده در خود logging) و تعریف هندلرها که در logging یا logging.handlers اعلامشدهاند، پیکربندی کنید.
- logging.config.dictConfig(config)¶
پیکربندی گزارش را از یک دیکشنری دریافت میکند. محتوای این دیکشنری در طرحوارهی دیکشنری پیکربندی در ادامه توضیح داده شده است.
اگر در حین پیکربندی خطایی رخ دهد، این تابع یک
ValueError،TypeError،AttributeErrorیاImportErrorرا با پیامی توصیفی و مناسب پرتاب میکند. در ادامه فهرستی (احتمالاً ناقص) از شرایطی آمده است که باعث پرتاب خطا میشوند:یک
levelکه رشته نباشد یا رشتهای باشد که با یک سطح گزارشدهی واقعی مطابقت نداشته باشد.یک مقدار
propagateکه بولی نیست.شناسهای که مقصد متناظری ندارد.
یک شناسهی هندلر ناموجود در حین یک فراخوانی افزایشی یافت شد.
نام گزارشگیر نامعتبر.
ناتوانی در ارجاع به یک شیء داخلی یا خارجی.
تجزیه توسط کلاس
DictConfiguratorانجام میشود؛ دیکشنری استفادهشده برای پیکربندی به سازندهی این کلاس ارسال میشود و این کلاس دارای یک متدconfigure()است. ماژولlogging.configدارای یک ویژگی قابل فراخوانی به نامdictConfigClassاست که در ابتدا بهDictConfiguratorتنظیم شده است. شما میتوانید مقدارdictConfigClassرا با پیادهسازی مناسبی از خودتان جایگزین کنید.dictConfig()،dictConfigClassرا با دیکشنری مشخصشده فراخوانی میکند و سپس متدconfigure()را روی شیء برگرداندهشده فراخوانی میکند تا پیکربندی را به اجرا درآورد:def dictConfig(config): dictConfigClass(config).configure()
برای مثال، زیرکلاسی از
DictConfiguratorمیتواندDictConfigurator.__init__()را در__init__()خود فراخوانی کند، سپس پیشوندهای سفارشی را تنظیم کند که در فراخوانی بعدیconfigure()قابلاستفاده خواهند بود.dictConfigClassبه این زیرکلاس جدید متصل میشود، و سپس میتوانdictConfig()را دقیقاً مانند حالت پیشفرض و سفارشینشده فراخوانی کرد.اضافه شده در نسخهی 3.2.
- logging.config.fileConfig(fname, defaults=None, disable_existing_loggers=True, encoding=None)¶
پیکربندی گزارشگیری را از پروندهای با قالب
configparser-format میخواند. قالب پرونده باید همانگونه باشد که در قالب پروندهی پیکربندی توضیح داده شده است. میتوان این تابع را چندین بار از یک برنامه فراخوانی کرد، تا به کاربر نهایی امکان دهد از میان پیکربندیهای مختلف از پیش تعریفشده انتخاب کند (اگر توسعهدهنده سازوکاری برای ارائه گزینهها و بارگذاری پیکربندی انتخابشده فراهم کند).اگر پرونده وجود نداشته باشد،
FileNotFoundErrorو اگر پرونده نامعتبر یا خالی باشد،RuntimeErrorپرتاب میشود.- پارامترها:
fname -- یک نام پرونده، یا یک شیء شبهپرونده، یا یک نمونه مشتقشده از
RawConfigParser. اگر یک نمونه مشتقشده ازRawConfigParserارسال شود، همانطور که هست استفاده میشود. در غیر این صورت، نمونهای ازConfigParserایجاد میشود، و پیکربندی توسط آن از شیء ارسالشده درfnameخوانده میشود. اگر آن شیء دارای متدreadline()باشد، فرض میشود که یک شیء شبهپرونده است و با استفاده ازread_file()خوانده میشود؛ در غیر این صورت، فرض میشود که یک نام پرونده است و بهread()ارسال میشود.defaults -- میتوان پیشفرضهایی را که باید به
ConfigParserداده شوند، در این آرگومان مشخص کرد.disable_existing_loggers -- اگر بهصورت
Falseمشخص شود، گزارشگیرهایی که در زمان انجام این فراخوانی وجود دارند، فعال باقی میمانند. مقدار پیشفرضTrueاست، زیرا این کار رفتار قدیمی را بهشکلی سازگار با عقبگرد فعال میکند. این رفتار، غیرفعال کردن هر گزارشگیر غیرریشهای موجود است، مگر اینکه نام خود آنها یا اجدادشان بهصراحت در پیکربندی گزارش ذکر شده باشد.encoding -- کدگذاری استفادهشده برای باز کردن پرونده هنگامی که fname نام پرونده است.
تغییر یافته در نسخهی 3.4: نمونهای از یک زیرکلاس از
RawConfigParserاکنون بهعنوان مقداری برایfnameپذیرفته میشود. این امر موارد زیر را تسهیل میکند:استفاده از یک پرونده پیکربندی که در آن پیکربندی گزارشگیری تنها بخشی از پیکربندی کلی برنامه است.
استفاده از پیکربندیای که از یک پرونده خوانده میشود و سپس توسط برنامهی استفادهکننده اصلاح میشود (مثلاً بر اساس پارامترهای خط فرمان یا سایر جنبههای محیط رانتایم)، پیش از آنکه به
fileConfigداده شود.
تغییر یافته در نسخهی 3.10: پارامتر encoding اضافه شد.
تغییر یافته در نسخهی 3.12: اگر پرونده ارائهشده وجود نداشته باشد یا نامعتبر یا خالی باشد، استثنایی پرتاب خواهد شد.
- logging.config.listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None)¶
یک سرور سوکت را روی پورت مشخصشده راهاندازی میکند و منتظر پیکربندیهای جدید میماند. اگر پورتی مشخص نشده باشد، از
DEFAULT_LOGGING_CONFIG_PORTپیشفرض ماژول استفاده میشود. پیکربندیهای گزارش بهعنوان پروندهای مناسب برای پردازش توسطdictConfig()یاfileConfig()ارسال میشوند. یک نمونه ازThreadبرمیگرداند که میتوانید برای راهاندازی سرور،start()را روی آن فراخوانی کنید و در زمان مناسب،join()را روی آن فراخوانی کنید. برای توقف سرور،stopListening()را فراخوانی کنید.آرگومان
verify، در صورت تعیین شدن، باید یک شیء فراخوانیپذیر باشد که تأیید کند آیا بایتهای دریافتی از طریق سوکت معتبر هستند و باید پردازش شوند. این کار میتواند با رمزنگاری و/یا امضای آنچه از طریق سوکت ارسال میشود انجام شود، بهگونهای که شیء فراخوانیپذیرverifyبتواند تأیید امضا و/یا رمزگشایی را انجام دهد. شیء فراخوانیپذیرverifyبا یک آرگومان فراخوانی میشود — بایتهای دریافتی از طریق سوکت — و باید بایتهایی را که باید پردازش شوند برگرداند، یاNoneرا برگرداند تا نشان دهد که بایتها باید دور ریخته شوند. بایتهای برگرداندهشده میتوانند همان بایتهای ورودی باشند (مثلاً زمانی که فقط تأیید انجام میشود)، یا میتوانند کاملاً متفاوت باشند (شاید در صورتی که رمزگشایی انجام شده باشد).برای ارسال یک پیکربندی به سوکت، پرونده پیکربندی را بخوانید و آن را به سوکت بهصورت دنبالهای از بایتها ارسال کنید که پیش از آن یک رشتهی طول چهاربایتی قرار دارد؛ این رشتهی طول بهصورت دودویی با استفاده از
struct.pack('>L', n)بستهبندی شده است.توجه
از آنجا که بخشهایی از پیکربندی از
eval()عبور داده میشوند، استفاده از این تابع ممکن است کاربران آن را در معرض یک خطر امنیتی قرار دهد. اگرچه این تابع فقط یک سوکت رویlocalhostرا مقید میکند و بنابراین اتصالات از ماشینهای راه دور را نمیپذیرد، اما سناریوهایی وجود دارد که کد غیرقابلاعتماد میتواند تحت حساب کاربری فرایندی کهlisten()را فراخوانی میکند اجرا شود. بهطور مشخص، اگر فرایندی کهlisten()را فراخوانی میکند روی یک ماشین چندکاربره اجرا شود که کاربران نمیتوانند به یکدیگر اعتماد کنند، یک کاربر مخرب میتواند صرفاً با اتصال به سوکتlisten()قربانی و ارسال پیکربندیای که هر کدی را که مهاجم بخواهد اجرا میکند، ترتیبی دهد که عملاً کد دلخواهی در فرایند کاربر قربانی اجرا شود. اگر پورت پیشفرض استفاده شود، این کار بهویژه آسان است، اما حتی اگر پورت متفاوتی استفاده شود نیز دشوار نیست. برای جلوگیری از خطر رخ دادن این حالت، از آرگومانverifyبرایlisten()استفاده کنید تا از اعمال پیکربندیهای ناشناخته جلوگیری شود.تغییر یافته در نسخهی 3.4: آرگومان
verifyافزوده شد.توجه
اگر میخواهید پیکربندیهایی را به شنونده ارسال کنید که گزارشگیرهای موجود را غیرفعال نمیکنند، باید از یک قالب JSON برای پیکربندی استفاده کنید که برای پیکربندی از
dictConfig()استفاده میکند. این روش به شما امکان میدهدdisable_existing_loggersرا بهعنوانFalseدر پیکربندیای که ارسال میکنید مشخص کنید.
ملاحظات امنیتی¶
قابلیت پیکربندی گزارشگیری تلاش میکند سهولت را فراهم کند، و این امر تا حدی با ارائه توانایی تبدیل متن موجود در پروندههای پیکربندی به اشیای پایتون مورد استفاده در پیکربندی گزارشگیری انجام میشود — برای مثال، همانطور که در اشیاء تعریفشده توسط کاربر توضیح داده شده است. با این حال، همین سازوکارها (ایمپورت کردن فراخوانیپذیرها از ماژولهای تعریفشده توسط کاربر و فراخوانی آنها با پارامترهایی از پیکربندی) میتوانند برای اجرای هر کدی که بخواهید به کار روند، و به همین دلیل باید با پروندههای پیکربندی از منابع نامطمئن با نهایت احتیاط رفتار کنید و پیش از بارگذاری واقعی آنها، اطمینان حاصل کنید که اگر آنها را بارگذاری کنید، هیچ اتفاق بدی نمیتواند رخ دهد.
طرحوارهی دیکشنری پیکربندی¶
توصیف یک پیکربندی گزارشگیری نیازمند فهرست کردن اشیای مختلفی است که باید ایجاد شوند و ارتباطات بین آنها؛ برای مثال، ممکن است یک هندلر به نام 'console' ایجاد کنید و سپس بگویید که گزارشگیر با نام 'startup' پیامهای خود را به هندلر 'console' ارسال میکند. این شیءها به آنهایی که ماژول logging ارائه میدهد محدود نمیشوند، زیرا ممکن است کلاس قالببند (formatter) یا هندلر خودتان را بنویسید. پارامترهای این کلاسها نیز ممکن است نیاز به شامل کردن اشیای خارجی مانند sys.stderr داشته باشند. سینتکس توصیف این شیءها و ارتباطات در ارتباطهای شیء در ادامه تعریف شده است.
جزئیات طرحوارهی دیکشنری¶
دیکشنری ارسالشده به dictConfig() باید شامل کلیدهای زیر باشد:
version - باید به یک مقدار عدد صحیح تنظیم شود که نشاندهندهی نسخهی طرحواره (schema) است. تنها مقدار معتبر در حال حاضر ۱ است، اما وجود این کلید به طرحواره اجازه میدهد تا تکامل یابد، در حالی که همچنان سازگاری با نسخههای پیشین حفظ میشود.
تمام کلیدهای دیگر اختیاری هستند، اما در صورت وجود، همانطور که در زیر توضیح داده شده است تفسیر میشوند. در تمام موارد زیر که یک «دیکشنری پیکربندی» ذکر شده است، وجود کلید ویژه '()' در آن بررسی میشود تا مشخص شود آیا نمونهسازی سفارشی لازم است یا خیر. در این صورت، از سازوکار توصیفشده در اشیاء تعریفشده توسط کاربر در زیر برای ایجاد یک نمونه استفاده میشود؛ در غیر این صورت، از زمینه برای تعیین آنچه باید نمونهسازی شود استفاده میشود.
formatters - مقدار متناظر یک دیکشنری خواهد بود که در آن هر کلید یک شناسه قالببند (formatter) است و هر مقدار یک دیکشنری است که چگونگی پیکربندی نمونه متناظر از
Formatterرا توصیف میکند.در دیکشنری پیکربندی، کلیدهای اختیاری زیر جستجو میشوند که با آرگومانهای ارسالشده برای ایجاد یک شیء
Formatterمتناظر هستند:formatdatefmtstylevalidate(از نسخه >=3.8)defaults(از نسخه >=3.12)
کلید اختیاری
classنام کلاس قالببند را مشخص میکند (بهصورت نام نقطهدار ماژول و کلاس). آرگومانهای نمونهسازی همانندFormatterهستند، بنابراین این کلید برای نمونهسازی یک زیرکلاس سفارشی ازFormatterبیشترین کاربرد را دارد. برای مثال، کلاس جایگزین ممکن است ردگیریهای پشتهی استثنا را در قالب گسترده یا فشرده ارائه دهد. اگر قالببند شما به کلیدهای پیکربندی متفاوت یا اضافی نیاز دارد، باید از اشیاء تعریفشده توسط کاربر استفاده کنید.filters - مقدار متناظر، یک دیکشنری خواهد بود که در آن هر کلید یک شناسه فیلتر است و هر مقدار یک دیکشنری است که چگونگی پیکربندی نمونه Filter مربوطه را توصیف میکند.
در دیکشنری پیکربندی به دنبال کلید
nameجستجو میشود (که مقدار پیشفرض آن رشته خالی است) و از آن برای ساخت یک نمونهlogging.Filterاستفاده میشود.handlers - مقدار متناظر یک دیکشنری خواهد بود که در آن هر کلید یک شناسهی handler و هر مقدار یک دیکشنری است که چگونگی پیکربندی نمونهی Handler متناظر را توصیف میکند.
دیکشنری پیکربندی برای کلیدهای زیر جستجو میشود:
class(الزامی). این نام کامل کلاس هندلر است.level(اختیاری). سطح هندلر .formatter(اختیاری). شناسهی قالببند (formatter) برای این هندلر .filters(اختیاری). فهرستی از شناسههای فیلترها برای این هندلر .تغییر یافته در نسخهی 3.11:
filtersمیتواند علاوه بر شناسهها، نمونههای فیلتر را نیز بپذیرد.
همهی کلیدهای دیگر بهعنوان آرگومانهای کلیدواژهای به سازندهی handler منتقل میشوند. برای مثال، با فرض قطعهکد:
handlers: console: class : logging.StreamHandler formatter: brief level : INFO filters: [allow_foo] stream : ext://sys.stdout file: class : logging.handlers.RotatingFileHandler formatter: precise filename: logconfig.log maxBytes: 1024 backupCount: 3
هندلر با شناسهی
consoleبهصورت یکlogging.StreamHandlerو با استفاده ازsys.stdoutبهعنوان جریان زیرین نمونهسازی میشود. هندلر با شناسهیfileبهصورت یکlogging.handlers.RotatingFileHandlerبا آرگومانهای کلیدواژهایfilename='logconfig.log', maxBytes=1024, backupCount=3نمونهسازی میشود.loggers - مقدار مربوطه یک دیکشنری خواهد بود که در آن هر کلید نام یک logger است و هر مقدار یک دیکشنری است که چگونگی پیکربندی نمونهی Logger مربوطه را توصیف میکند.
دیکشنری پیکربندی برای کلیدهای زیر جستجو میشود:
level(اختیاری). سطح گزارشگیر .propagate(اختیاری). تنظیم انتشار گزارشگیر .filters(اختیاری). فهرستی از شناسههای فیلترها برای این logger.تغییر یافته در نسخهی 3.11:
filtersمیتواند علاوه بر شناسهها، نمونههای فیلتر را نیز بپذیرد.handlers(اختیاری). فهرستی از شناسههای هندلرها برای این گزارشگیر .
گزارشگیرهای مشخصشده بر اساس سطح، انتشار، فیلترها و هندلرهای مشخصشده پیکربندی خواهند شد.
root - این پیکربندی برای گزارشگیر ریشه خواهد بود. پردازش پیکربندی همانند هر گزارشگیر دیگری خواهد بود، بهجز این که تنظیم
propagateقابل اعمال نخواهد بود.incremental - اینکه آیا پیکربندی باید بهصورت افزایشی نسبت به پیکربندی موجود تفسیر شود یا خیر. این مقدار بهطور پیشفرض
Falseاست، به این معنا که پیکربندی مشخصشده جایگزین پیکربندی موجود میشود، با همان معناشناسی که API موجودfileConfig()از آن استفاده میکند.اگر مقدار تعیینشده
Trueباشد، پیکربندی همانگونه که در بخش پیکربندی افزایشی توضیح داده شده است، پردازش میشود.disable_existing_loggers - اینکه آیا گزارشگیرهای غیرریشهای موجود باید غیرفعال شوند. این تنظیم مشابه پارامتری با همین نام در
fileConfig()است. در صورت عدم وجود، مقدار پیشفرض این پارامترTrueاست. اگر incremental برابرTrueباشد، این مقدار نادیده گرفته میشود.
پیکربندی افزایشی¶
فراهم کردن انعطافپذیری کامل برای پیکربندی افزایشی دشوار است. برای مثال، از آنجا که اشیایی مانند فیلترها و قالببندها ناشناس هستند، پس از برپایی یک پیکربندی، امکان ارجاع به چنین اشیای ناشناسی هنگام گسترش یک پیکربندی وجود ندارد.
علاوه بر این، پس از تنظیم یک پیکربندی، دلیل قانعکنندهای برای تغییر دلخواهانهی گراف اشیای گزارشگیرها، هندلرها، فیلترها و قالببندها (formatters) در رانتایم وجود ندارد؛ میزان جزئیات گزارشگیرها و هندلرها را میتوان صرفاً با تنظیم سطحها (و در مورد گزارشگیرها، پرچمهای انتشار) کنترل کرد. تغییر دلخواهانهی گراف اشیاء بهشکلی امن در محیط چندنخی مشکلساز است؛ اگرچه غیرممکن نیست، اما مزایای آن، پیچیدگیای را که به پیادهسازی اضافه میکند توجیه نمیکند.
بنابراین، هنگامی که کلید incremental در یک دیکشنری پیکربندی وجود داشته باشد و True باشد، سیستم بهطور کامل هر ورودیِ formatters و filters را نادیده میگیرد و فقط تنظیمات level را در ورودیهای handlers و تنظیمات level و propagate را در ورودیهای loggers و root پردازش میکند.
با استفاده از یک مقدار در دیکشنری پیکربندی، میتوان پیکربندیها را بهصورت دیکشنریهای pickleشده بر بستر شبکه به یک شنوندهی سوکت ارسال کرد. بنابراین، سطح جزئیات گزارشدهی یک برنامهی طولانیمدت را میتوان در طول زمان تغییر داد، بدون نیاز به توقف و راهاندازی مجدد برنامه.
ارتباطهای شیء¶
طرحواره مجموعهای از اشیای گزارشگیری - گزارشگیرها، هندلرها، قالببندها، فیلترها - را توصیف میکند که در یک گراف اشیاء به یکدیگر متصل شدهاند. بنابراین، طرحواره باید اتصالهای بین اشیاء را نمایش دهد. برای مثال، فرض کنید پس از پیکربندی، یک هندلر خاص به یک گزارشگیر خاص متصل شده است. برای اهداف این بحث، میتوان گفت که در یک اتصال بین این دو، گزارشگیر نمایانگر مبدأ و هندلر نمایانگر مقصد است. البته در اشیای پیکربندیشده، این موضوع به این صورت نمایش داده میشود که گزارشگیر یک ارجاع به هندلر نگهداری میکند. در دیکشنری پیکربندی، این کار با اختصاص یک شناسه به هر شیء مقصد انجام میشود که آن را بدون ابهام شناسایی میکند، و سپس با استفاده از آن شناسه در پیکربندی شیء مبدأ نشان داده میشود که یک اتصال بین شیء مبدأ و شیء مقصد با آن شناسه وجود دارد.
بنابراین، برای مثال، قطعهکد YAML زیر را در نظر بگیرید:
formatters:
brief:
# configuration for formatter with id 'brief' goes here
precise:
# configuration for formatter with id 'precise' goes here
handlers:
h1: #This is an id
# configuration of handler with id 'h1' goes here
formatter: brief
h2: #This is another id
# configuration of handler with id 'h2' goes here
formatter: precise
loggers:
foo.bar.baz:
# other configuration for logger 'foo.bar.baz'
handlers: [h1, h2]
(توجه: اینجا از YAML استفاده شده است، زیرا کمی از شکل معادل منبع پایتون برای دیکشنری خواناتر است.)
شناسههای گزارشگیرها همان نامهای گزارشگیر هستند که برای به دست آوردن ارجاع به آن گزارشگیرها بهصورت برنامهای استفاده میشوند، برای مثال foo.bar.baz. شناسههای قالببندها و فیلترها میتوانند هر مقدار رشتهای باشند (مانند brief، precise در بالا) و گذرا هستند، به این معنا که فقط برای پردازش دیکشنری پیکربندی معنادارند و برای تعیین ارتباط بین اشیاء استفاده میشوند، و پس از کامل شدن فراخوانی پیکربندی، هیچجا پایا نمیشوند.
قطعهکد بالا نشان میدهد که گزارشگیری با نام foo.bar.baz باید دو هندلر متصل به آن داشته باشد، که این هندلرها با شناسههای هندلر h1 و h2 توصیف شدهاند. قالببند (formatter) برای h1 همان چیزی است که با شناسه brief توصیف شده است، و قالببند برای h2 همان چیزی است که با شناسه precise توصیف شده است.
اشیاء تعریفشده توسط کاربر¶
این طرحواره از اشیای تعریفشده توسط کاربر برای هندلرها، فیلترها و قالببندها پشتیبانی میکند. (گزارشگیرها نیازی ندارند که برای نمونههای مختلف، انواع مختلفی داشته باشند، بنابراین در این طرحوارهی پیکربندی، از کلاسهای گزارشگیر تعریفشده توسط کاربر پشتیبانی نمیشود.)
اشیایی که باید پیکربندی شوند، با دیکشنریهایی توصیف میشوند که جزئیات پیکربندی آنها را شرح میدهند. در برخی موارد، سامانهی گزارشگیری میتواند از روی زمینه استنباط کند که یک شیء چگونه باید نمونهسازی شود، اما هنگامی که قرار است یک شیء تعریفشده توسط کاربر نمونهسازی شود، سامانه نمیداند این کار را چگونه انجام دهد. برای فراهم کردن انعطافپذیری کامل برای نمونهسازی اشیاء تعریفشده توسط کاربر، کاربر باید یک «کارخانه» فراهم کند؛ یک شیء فراخوانیپذیر که با یک دیکشنری پیکربندی فراخوانی میشود و شیء نمونهسازیشده را بازمیگرداند. این موضوع با در دسترس قرار گرفتن یک مسیر ایمپورت مطلق به کارخانه تحت کلید ویژه '()' مشخص میشود. در ادامه یک مثال ملموس آمده است:
formatters:
brief:
format: '%(message)s'
default:
format: '%(asctime)s %(levelname)-8s %(name)-15s %(message)s'
datefmt: '%Y-%m-%d %H:%M:%S'
custom:
(): my.package.customFormatterFactory
bar: baz
spam: 99.9
answer: 42
قطعهی YAML بالا سه قالببند را تعریف میکند. نخستین قالببند، با شناسهی brief، یک نمونهی استاندارد از logging.Formatter با رشتهی قالب مشخصشده است. دومین قالببند، با شناسهی default، قالب طولانیتری دارد و همچنین قالب زمان را بهصراحت تعریف میکند و به نمونهای از logging.Formatter منجر میشود که با آن دو رشتهی قالب مقداردهی اولیهشده است. هنگامی که به شکل کد منبع پایتون نمایش داده شوند، قالببندهای brief و default دارای زیردیکشنریهای پیکربندی هستند:
{
'format' : '%(message)s'
}
و:
{
'format' : '%(asctime)s %(levelname)-8s %(name)-15s %(message)s',
'datefmt' : '%Y-%m-%d %H:%M:%S'
}
بهترتیب، و از آنجا که این دیکشنریها حاوی کلید ویژه '()' نیستند، نمونهسازی از زمینه استنتاج میشود: در نتیجه، نمونههای استاندارد logging.Formatter ایجاد میشوند. زیردیکشنری پیکربندی سومین قالببند، با شناسه custom، به این صورت است:
{
'()' : 'my.package.customFormatterFactory',
'bar' : 'baz',
'spam' : 99.9,
'answer' : 42
}
و این شامل کلید ویژه '()' است، که به این معناست که نمونهسازی تعریفشده توسط کاربر مطلوب است. در این حالت، از فراخوانیپذیر کارخانهای مشخصشده استفاده خواهد شد. اگر واقعاً یک فراخوانیپذیر باشد، بهصورت مستقیم استفاده خواهد شد؛ در غیر این صورت، اگر یک رشته (مانند مثال) مشخص کنید، فراخوانیپذیر واقعی با استفاده از سازوکارهای معمول ایمپورت پیدا خواهد شد. این فراخوانیپذیر با آیتمهای باقیمانده در زیردیکشنری پیکربندی بهعنوان آرگومانهای کلیدواژهای فراخوانی خواهد شد. در مثال بالا، فرض میشود که قالببند با شناسه custom از فراخوانی برگردانده شده باشد:
my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42)
هشدار
مقادیر کلیدهایی مانند bar، spam و answer در مثال بالا نباید دیکشنریهای پیکربندی یا ارجاعهایی مانند cfg://foo یا ext://bar باشند، زیرا توسط سازوکار پیکربندی پردازش نمیشوند، بلکه بههمانصورت به فراخوانیپذیر منتقل میشوند.
کلید '()' بهعنوان کلید ویژه استفاده شده است، زیرا نام پارامتر کلیدواژهای معتبری نیست و بنابراین با نام آرگومانهای کلیدواژهای استفادهشده در فراخوانی تداخل نخواهد داشت. '()' همچنین بهعنوان یک یادآور عمل میکند که مقدار متناظر فراخوانیپذیر است.
تغییر یافته در نسخهی 3.11: عضو filters در handlers و loggers میتواند علاوه بر شناسهها، نمونههای فیلتر را نیز بپذیرد.
همچنین میتوانید یک کلید ویژه '.' مشخص کنید که مقدار آن نگاشتی از نام ویژگیها به مقادیر است. در صورت یافت شدن، ویژگیهای مشخصشده پیش از بازگرداندن شیء تعریفشده توسط کاربر، روی آن تنظیم میشوند. بنابراین، با پیکربندی زیر:
{
'()' : 'my.package.customFormatterFactory',
'bar' : 'baz',
'spam' : 99.9,
'answer' : 42,
'.' : {
'foo': 'bar',
'baz': 'bozz'
}
}
قالببند برگرداندهشده دارای ویژگی foo با مقدار 'bar' و ویژگی baz با مقدار 'bozz' خواهد بود.
هشدار
مقادیر ویژگیهایی مانند foo و baz در مثال بالا نباید دیکشنریهای پیکربندی یا ارجاعهایی مانند cfg://foo یا ext://bar باشند، زیرا توسط سازوکار پیکربندی پردازش نمیشوند، بلکه همانطور که هستند بهعنوان مقادیر ویژگی تنظیم میشوند.
ترتیب پیکربندی هندلر¶
هندلرها به ترتیب الفبایی کلیدهایشان پیکربندی میشوند، و هندلر پیکربندیشده جایگزین دیکشنری پیکربندی در (یک نسخه کاری از) دیکشنری handlers در طرحواره میشود. اگر از ساختاری مانند cfg://handlers.foo استفاده کنید، در ابتدا handlers['foo'] به دیکشنری پیکربندی برای هندلری با نام foo اشاره میکند، و بعداً (پس از پیکربندی آن هندلر) به نمونه هندلر پیکربندیشده اشاره میکند. بنابراین، cfg://handlers.foo میتواند به یک دیکشنری یا یک نمونه هندلر حل شود. بهطور کلی، عاقلانه است که هندلرها را به گونهای نامگذاری کنید که هندلرهای وابسته پس از هر هندلری که به آن وابستهاند پیکربندی شوند؛ این کار اجازه میدهد که چیزی مانند cfg://handlers.foo در پیکربندی هندلری که به هندلر foo وابسته است استفاده شود. اگر آن هندلر وابسته bar نامیده شود، مشکلاتی ایجاد خواهد شد، زیرا تلاش برای پیکربندی bar پیش از پیکربندی foo انجام خواهد شد و foo هنوز پیکربندی نشده خواهد بود. با این حال، اگر هندلر وابسته foobar نامیده شود، پس از foo پیکربندی خواهد شد، در نتیجه cfg://handlers.foo به هندلر پیکربندیشده foo حل خواهد شد، نه به دیکشنری پیکربندی آن.
دسترسی به اشیاء خارجی¶
مواردی وجود دارد که یک پیکربندی باید به اشیای خارج از پیکربندی ارجاع دهد، برای مثال sys.stderr. اگر دیکشنری پیکربندی با استفاده از کد پایتون ساخته شود، این کار ساده است، اما هنگامی که پیکربندی از طریق یک پرونده متنی (مثلاً JSON، YAML) ارائه میشود، مشکلی پیش میآید. در یک پرونده متنی، هیچ روش استانداردی برای تمایز sys.stderr از رشتهی لفظی 'sys.stderr' وجود ندارد. برای تسهیل این تمایز، سامانهی پیکربندی به دنبال برخی پیشوندهای ویژه در مقادیر رشتهای میگردد و با آنها بهطور ویژه رفتار میکند. برای مثال، اگر رشتهی لفظی 'ext://sys.stderr' بهعنوان یک مقدار در پیکربندی ارائه شود، ext:// حذف میشود و باقیماندهی مقدار با استفاده از سازوکارهای معمول ایمپورت پردازش میشود.
مدیریت چنین پیشوندهایی به شیوهای مشابه مدیریت پروتکل انجام میشود: سازوکاری عام برای جستوجوی پیشوندهایی وجود دارد که با عبارت باقاعده ^(?P<prefix>[a-z]+)://(?P<suffix>.*)$ مطابقت دارند؛ به موجب آن، اگر prefix شناسایی شود، suffix به شیوهای وابسته به پیشوند پردازش میشود و نتیجهی پردازش جایگزین مقدار رشته میشود. اگر پیشوند شناسایی نشود، مقدار رشته بدون تغییر باقی میماند.
دسترسی به اشیاء داخلی¶
علاوه بر اشیاء خارجی، گاهی نیز نیاز است به اشیایی در پیکربندی ارجاع داده شود. این کار برای مواردی که سیستم پیکربندی آنها را میشناسد، بهطور ضمنی توسط سیستم پیکربندی انجام میشود. برای مثال، مقدار رشتهای 'DEBUG' برای یک level در یک گزارشگیر یا هندلر بهطور خودکار به مقدار logging.DEBUG تبدیل میشود، و ورودیهای handlers، filters و formatter یک شناسهی شیء را میپذیرند و به شیء مقصد مناسب ارجاع میدهند.
با این حال، برای اشیای تعریفشده توسط کاربر که برای ماژول logging شناختهشده نیستند، به سازوکار عامتری نیاز است. برای مثال، logging.handlers.MemoryHandler را در نظر بگیرید که یک آرگومان target میگیرد؛ این آرگومان یک هندلر دیگر است که کار به آن واگذار میشود. از آنجا که سیستم از پیش این کلاس را میشناسد، در پیکربندی، target دادهشده تنها باید شناسهی شیء مدیر هدف مربوطه باشد و سیستم هندلر را از روی شناسه حل خواهد کرد. اما اگر کاربری my.package.MyHandler را تعریف کند که یک هندلر جایگزین با نام alternate دارد، سیستم پیکربندی نمیداند که alternate به یک هندلر اشاره دارد. برای پاسخ به این حالت، یک سیستم حل عام به کاربر اجازه میدهد که مشخص کند:
handlers:
file:
# configuration of file handler goes here
custom:
(): my.package.MyHandler
alternate: cfg://handlers.file
رشتهی لفظی 'cfg://handlers.file' به شیوهای مشابه رشتههای دارای پیشوند ext:// حل میشود، اما به جای فضای نام ایمپورت، در خود پیکربندی جستجو میکند. این سازوکار امکان دسترسی از طریق نقطه یا اندیس را، به شیوهای مشابه آنچه str.format فراهم میکند، میدهد. بنابراین، با توجه به قطعهکد زیر:
handlers:
email:
class: logging.handlers.SMTPHandler
mailhost: localhost
fromaddr: my_app@domain.tld
toaddrs:
- support_team@domain.tld
- dev_team@domain.tld
subject: Houston, we have a problem.
در پیکربندی، رشتهی 'cfg://handlers' به دیکشنری با کلید handlers حل میشود، رشتهی 'cfg://handlers.email به دیکشنری با کلید email در دیکشنری handlers حل میشود، و به همین ترتیب. رشتهی 'cfg://handlers.email.toaddrs[1] به 'dev_team@domain.tld' حل میشود و رشتهی 'cfg://handlers.email.toaddrs[0]' به مقدار 'support_team@domain.tld' حل میشود. میتوانید به مقدار subject با استفاده از 'cfg://handlers.email.subject' یا، بهطور معادل، 'cfg://handlers.email[subject]' دسترسی پیدا کنید. شکل دوم تنها زمانی لازم است استفاده شود که کلید شامل فاصله یا نویسههای غیرالفبایی-عددی باشد. توجه داشته باشید که نویسههای [ و ] در کلیدها مجاز نیستند. اگر مقدار اندیس فقط از ارقام دهدهی تشکیل شده باشد، تلاش میشود با استفاده از مقدار عدد صحیح متناظر دسترسی انجام شود و در صورت نیاز، به مقدار رشتهای بازمیگردد.
با فرض رشتهی cfg://handlers.myhandler.mykey.123، این رشته به config_dict['handlers']['myhandler']['mykey']['123'] حل میشود. اگر رشته بهصورت cfg://handlers.myhandler.mykey[123] مشخص شده باشد، سیستم تلاش میکند مقدار را از config_dict['handlers']['myhandler']['mykey'][123] بازیابی کند و در صورت شکست، به config_dict['handlers']['myhandler']['mykey']['123'] بازمیگردد.
حل ایمپورت و ایمپورتکنندههای سفارشی¶
بهطور پیشفرض، فرایند حل ایمپورت برای انجام ایمپورت از تابع توکار __import__() استفاده میکند. شاید بخواهید این را با سازوکار ایمپورت خودتان جایگزین کنید: در این صورت، میتوانید ویژگی importer در کلاس DictConfigurator یا ابرکلاس آن، کلاس BaseConfigurator را جایگزین کنید. با این حال، به دلیل نحوهی دسترسی به توابع از کلاسها از طریق توصیفگرها، باید مراقب باشید. اگر از یک شیء فراخوانیپذیر پایتون برای انجام ایمپورتهای خود استفاده میکنید و میخواهید آن را در سطح کلاس به جای سطح نمونه تعریف کنید، باید آن را با staticmethod() بپیچید. برای مثال:
from importlib import import_module
from logging.config import BaseConfigurator
BaseConfigurator.importer = staticmethod(import_module)
اگر فراخوانیپذیر مربوط به ایمپورت را بر روی یک نمونه از پیکربند (configurator) تنظیم میکنید، نیازی نیست آن را با staticmethod() بپوشانید.
پیکربندی QueueHandler و QueueListener¶
اگر میخواهید یک QueueHandler را پیکربندی کنید، با توجه به اینکه این مورد معمولاً همراه با یک QueueListener استفاده میشود، میتوانید هر دو را با هم پیکربندی کنید. پس از پیکربندی، نمونهی QueueListener بهعنوان ویژگی listener در هندلر ایجادشده در دسترس خواهد بود و این هندلر نیز به نوبه خود با استفاده از getHandlerByName() و ارسال نامی که برای QueueHandler در پیکربندی خود استفاده کردهاید، در دسترس شما قرار خواهد گرفت. طرحوارهی دیکشنری برای پیکربندی این جفت در قطعه نمونه YAML زیر نشان داده شده است.
handlers:
qhand:
class: logging.handlers.QueueHandler
queue: my.module.queue_factory
listener: my.package.CustomListener
handlers:
- hand_name_1
- hand_name_2
...
کلیدهای queue و listener اختیاری هستند.
اگر کلید queue وجود داشته باشد، مقدار متناظر میتواند یکی از موارد زیر باشد:
شیءای که API عمومی
Queue.put_nowaitوQueue.getرا پیادهسازی میکند. برای نمونه، این ممکن است یک نمونه واقعی ازqueue.Queueیا زیرکلاسی از آن، یا یک پراکسی بهدستآمده از طریقmultiprocessing.managers.SyncManager.Queue()باشد.البته این کار تنها در صورتی امکانپذیر است که در حال ساخت یا تغییر دیکشنری پیکربندی در کد باشید.
رشتهای که به یک فراخوانیپذیر ارجاع میدهد و هنگامی که بدون آرگومان فراخوانی شود، نمونه صف مورد استفاده را برمیگرداند. آن فراخوانیپذیر میتواند یک زیرکلاس از
queue.Queueیا تابعی باشد که یک نمونه صف مناسب را برمیگرداند، مانندmy.module.queue_factory().یک دیکشنری با کلید
'()'که به روش معمول، همانطور که در اشیاء تعریفشده توسط کاربر توضیح داده شد، ساخته میشود. نتیجهی این ساخت باید یک نمونه ازqueue.Queueباشد.
اگر کلید queue وجود نداشته باشد، یک نمونه استاندارد و بدون کران از queue.Queue ایجاد و استفاده میشود.
اگر کلید listener موجود باشد، مقدار متناظر میتواند یکی از موارد زیر باشد:
زیرکلاسی از
logging.handlers.QueueListener. البته این کار تنها در صورتی ممکن است که در حال ساخت یا تغییر دیکشنری پیکربندی در کد باشید.رشتهای که به کلاسی از زیرکلاسهای
QueueListenerحل میشود، مانند'my.package.CustomListener'.یک دیکشنری با کلید
'()'که به روش معمول، همانطور که در اشیاء تعریفشده توسط کاربر بحث شده است، ساخته میشود. نتیجه این ساخت باید یک شیء فراخوانیپذیر با همان امضای مقداردهنده اولیهQueueListenerباشد.
اگر کلید listener وجود نداشته باشد، از logging.handlers.QueueListener استفاده میشود.
مقادیر زیر کلید handlers، نام هندلرهای دیگر در پیکربندی هستند (که در قطعهکد بالا نشان داده نشدهاند) و به شنونده صف (queue listener) داده میشوند.
هرگونه کلاس سفارشی مدیر صف (queue handler) و شنونده باید با همان امضاهای مقداردهی اولیهی QueueHandler و QueueListener تعریف شود.
اضافه شده در نسخهی 3.12.
قالب پروندهی پیکربندی¶
قالب پرونده پیکربندی قابلفهم برای fileConfig() بر پایهی قابلیتهای configparser است. پرونده باید شامل بخشهایی به نامهای [loggers]، [handlers] و [formatters] باشد که موجودیتهای هر نوع تعریفشده در پرونده را با نام شناسایی میکنند. برای هر چنین موجودیتی، بخش جداگانهای وجود دارد که نحوهی پیکربندی آن موجودیت را مشخص میکند. بنابراین، برای یک گزارشگیر به نام log01 در بخش [loggers]، جزئیات پیکربندی مرتبط در بخش [logger_log01] نگهداری میشود. بهطور مشابه، پیکربندی یک هندلر به نام hand01 در بخش [handlers]، در بخشی به نام [handler_hand01] نگهداری میشود، در حالی که پیکربندی یک قالببند (formatter) به نام form01 در بخش [formatters]، در بخشی به نام [formatter_form01] مشخص میشود. پیکربندی گزارشگیر ریشه باید در بخشی به نام [logger_root] مشخص شود.
توجه
API fileConfig() قدیمیتر از API dictConfig() است و قابلیت پوشش برخی جنبههای گزارشگیری را فراهم نمیکند. برای مثال، با استفاده از fileConfig() نمیتوانید اشیای Filter را، که امکان فیلتر کردن پیامها فراتر از سطحهای عدد صحیح ساده را فراهم میکنند، پیکربندی کنید. اگر نیاز دارید نمونههایی از Filter در پیکربندی گزارشگیری خود داشته باشید، باید از dictConfig() استفاده کنید. توجه داشته باشید که بهبودهای آینده در قابلیت پیکربندی به dictConfig() افزوده خواهند شد، بنابراین ارزش دارد که هرگاه انجام این کار برایتان مقدور باشد، مهاجرت به این API جدیدتر را در نظر بگیرید.
مثالهایی از این بخشهای پرونده در زیر آمده است.
[loggers]
keys=root,log02,log03,log04,log05,log06,log07
[handlers]
keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09
[formatters]
keys=form01,form02,form03,form04,form05,form06,form07,form08,form09
گزارشگیر ریشه باید یک سطح و فهرستی از هندلرها را مشخص کند. در زیر نمونهای از یک بخش گزارشگیر ریشه آمده است.
[logger_root]
level=NOTSET
handlers=hand01
مدخل level میتواند یکی از DEBUG, INFO, WARNING, ERROR, CRITICAL یا NOTSET باشد. تنها برای گزارشگیر ریشه، NOTSET به این معناست که همهی پیامها ثبت خواهند شد. مقادیر سطح در زمینهی فضای نام بستهی logging ارزیابی میشوند.
ورودی handlers فهرستی جداشده با کاما از نامهای هندلر است که باید در بخش [handlers] ذکر شوند. این نامها باید در بخش [handlers] ذکر شوند و دارای بخشهای متناظر در پرونده پیکربندی باشند.
برای گزارشگیرهایی غیر از گزارشگیر ریشه، به اطلاعات بیشتری نیاز است. این موضوع در مثال زیر نشان داده شده است.
[logger_parser]
level=DEBUG
handlers=hand01
propagate=1
qualname=compiler.parser
ورودیهای level و handlers همانند گزارشگیر ریشه تفسیر میشوند، مگر اینکه سطح یک گزارشگیر غیرریشه بهصورت NOTSET مشخص شده باشد؛ در این صورت سیستم به گزارشگیرهای بالاتر در سلسلهمراتب رجوع میکند تا سطح مؤثر گزارشگیر را تعیین کند. ورودی propagate روی ۱ تنظیم میشود تا نشان دهد پیامها باید از این گزارشگیر به هندلرها در سطوح بالاتر سلسلهمراتب گزارشگیرها منتشر شوند، یا روی ۰ تنظیم میشود تا نشان دهد پیامها به هندلرهای بالاتر در سلسلهمراتب منتشر نمیشوند. ورودی qualname نام کانال سلسلهمراتبی گزارشگیر است، یعنی نامی که برنامه برای دریافت گزارشگیر از آن استفاده میکند.
بخشهایی که پیکربندی هندلر را مشخص میکنند، در ادامه نمونهسازی شدهاند.
[handler_hand01]
class=StreamHandler
level=NOTSET
formatter=form01
args=(sys.stdout,)
مدخل class کلاس هندلر را نشان میدهد (همانطور که بهوسیلهی eval() در فضای نام بستهی logging تعیین میشود). level به همان شیوهای که برای گزارشگیرها تفسیر میشود، تفسیر میشود، و NOTSET به معنای «گزارش کردن همهچیز» در نظر گرفته میشود.
مدخل formatter نام کلید قالببند برای این هندلر را نشان میدهد. اگر خالی باشد، از یک قالببند پیشفرض (logging._defaultFormatter) استفاده میشود. اگر نامی مشخصشده باشد، آن نام باید در بخش [formatters] ذکر شود و باید یک بخش متناظر در پرونده پیکربندی برای آن وجود داشته باشد.
آیتم args، هنگامی که در زمینهی فضای نام بستهی logging ارزیابی شود، فهرست آرگومانهای سازندهی کلاس هندلر است. برای مشاهدهی نحوهی ساخت آیتمهای معمول، به سازندههای هندلرهای مرتبط یا مثالهای زیر مراجعه کنید. اگر ارائه نشود، مقدار پیشفرض آن () خواهد بود.
آیتم اختیاری kwargs، هنگامی که در زمینهی فضای نام بستهی logging ارزیابی میشود، دیکشنری آرگومانهای کلیدواژهای برای سازندهی کلاس هندلر است. اگر ارائه نشود، مقدار پیشفرض آن {} است.
[handler_hand02]
class=FileHandler
level=DEBUG
formatter=form02
args=('python.log', 'w')
[handler_hand03]
class=handlers.SocketHandler
level=INFO
formatter=form03
args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT)
[handler_hand04]
class=handlers.DatagramHandler
level=WARN
formatter=form04
args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT)
[handler_hand05]
class=handlers.SysLogHandler
level=ERROR
formatter=form05
args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER)
[handler_hand06]
class=handlers.NTEventLogHandler
level=CRITICAL
formatter=form06
args=('Python Application', '', 'Application')
[handler_hand07]
class=handlers.SMTPHandler
level=WARN
formatter=form07
args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject')
kwargs={'timeout': 10.0}
[handler_hand08]
class=handlers.MemoryHandler
level=NOTSET
formatter=form08
target=
args=(10, ERROR)
[handler_hand09]
class=handlers.HTTPHandler
level=NOTSET
formatter=form09
args=('localhost:9022', '/log', 'GET')
kwargs={'secure': True}
بخشهایی که پیکربندی قالببند را مشخص میکنند، با نمونه زیر نشان داده میشوند.
[formatter_form01]
format=F1 %(asctime)s %(levelname)s %(message)s %(customfield)s
datefmt=
style=%
validate=True
defaults={'customfield': 'defaultvalue'}
class=logging.Formatter
آرگومانهای پیکربندی قالببند (formatter) همان کلیدهای موجود در طرحواره دیکشنری در بخش قالببندها هستند.
ورودی defaults، هنگام ارزیابی در زمینهی فضای نام بستهی logging، یک دیکشنری از مقادیر پیشفرض برای فیلدهای قالببندی سفارشی است. اگر ارائه نشود، بهطور پیشفرض None خواهد بود.
توجه
به دلیل استفاده از eval() همانطور که در بالا توضیح داده شد، خطرات امنیتی بالقوهای وجود دارد که از استفاده از listen() برای ارسال و دریافت پیکربندیها از طریق سوکتها ناشی میشود. این خطرات محدود به شرایطی است که چندین کاربر بدون اعتماد متقابل، کد را روی یک ماشین اجرا میکنند؛ برای اطلاعات بیشتر، مستندات listen() را ببینید.
همچنین ملاحظه نمائید
- ماژول
logging مرجع API برای ماژول logging.
- ماژول
logging.handlers هندلرهای مفید موجود در ماژول logging.