Loguru هي مكتبة تهدف إلى إحضار تسجيل ممتع في Python.
هل شعرت يومًا بالكسول تجاه تكوين مسجل واستخدام print()
بدلاً من ذلك؟ ... لقد فعلت ذلك ، ومع ذلك ، فإن التسجيل أمر أساسي لكل تطبيق ويخفف من عملية تصحيح الأخطاء. باستخدام Loguru ، ليس لديك عذر لعدم استخدام التسجيل من البداية ، وهذا بسيط مثل from loguru import logger
.
أيضًا ، تهدف هذه المكتبة إلى جعل تسجيل Python أقل إيلامًا عن طريق إضافة مجموعة من الوظائف المفيدة التي تحل المحاذير من عمليات تسجيل الدخول القياسية. يجب أن يكون استخدام السجلات في التطبيق الخاص بك بمثابة تلقائي ، يحاول Loguru جعلها ممتعة وقوية.
pip install loguru
المفهوم الرئيسي لـ Loguru هو أن هناك logger
واحدًا واحدًا فقط .
للراحة ، يتم تكوينه مسبقًا وإخراج stderr
(ولكن هذا قابل للتكوين بالكامل).
from loguru import logger
logger . debug ( "That's it, beautiful and simple logging!" )
logger
هو مجرد واجهة ترسل رسائل السجل إلى معالجات تكوينها. بسيط ، أليس كذلك؟
كيف تضيف معالج؟ كيفية إعداد تنسيق سجلات؟ كيفية تصفية الرسائل؟ كيف تضع المستوى؟
إجابة واحدة: وظيفة add()
.
logger . add ( sys . stderr , format = "{time} {level} {message}" , filter = "my_module" , level = "INFO" )
يجب استخدام هذه الوظيفة لتسجيل الأحواض المسؤولة عن إدارة رسائل السجل السياقية مع قول السجل. يمكن أن يأخذ الحوض العديد من النماذج: وظيفة بسيطة أو مسار سلسلة أو كائن يشبه الملف أو وظيفة coroutine أو معالج مدمج.
لاحظ أنه يمكنك أيضًا remove()
معالج تمت إضافته مسبقًا باستخدام المعرف الذي تم إرجاعه أثناء إضافته. يعد هذا مفيدًا بشكل خاص إذا كنت ترغب في حل معالج stderr
الافتراضي: ما عليك سوى استدعاء logger.remove()
لبدء بداية جديدة.
إذا كنت ترغب في إرسال الرسائل المسجلة إلى ملف ، فيجب عليك فقط استخدام مسار السلسلة مثل الحوض. يمكن توقيته تلقائيًا أيضًا للراحة:
logger . add ( "file_{time}.log" )
يمكن أيضًا تكوينه بسهولة إذا كنت بحاجة إلى مسجل دوار ، أو إذا كنت ترغب في إزالة السجلات القديمة ، أو إذا كنت ترغب في ضغط ملفاتك عند الإغلاق.
logger . add ( "file_1.log" , rotation = "500 MB" ) # Automatically rotate too big file
logger . add ( "file_2.log" , rotation = "12:00" ) # New file is created each day at noon
logger . add ( "file_3.log" , rotation = "1 week" ) # Once the file is too old, it's rotated
logger . add ( "file_X.log" , retention = "10 days" ) # Cleanup after some time
logger . add ( "file_Y.log" , compression = "zip" ) # Save some loved space
Loguru تفضل التنسيق الأكثر أناقة وقوة {}
أكثر من %
، وظائف التسجيل تعادل في الواقع str.format()
.
logger . info ( "If you're using Python {}, prefer {feature} of course!" , 3.6 , feature = "f-strings" )
هل سبق لك أن رأيت برنامجك يتعطل بشكل غير متوقع دون رؤية أي شيء في ملف السجل؟ هل سبق لك أن لاحظت أنه لم يتم تسجيل الاستثناءات التي تحدث في المواضيع؟ يمكن حل هذا باستخدام مدير الديكور / السياق catch()
والذي يضمن نشر أي خطأ بشكل صحيح إلى logger
.
@ logger . catch
def my_function ( x , y , z ):
# An error? It's caught anyway!
return 1 / ( x + y + z )
يضيف Loguru تلقائيًا الألوان إلى سجلاتك إذا كانت المحطة الخاصة بك متوافقة. يمكنك تحديد أسلوبك المفضل باستخدام علامات الترميز بتنسيق الحوض.
logger . add ( sys . stdout , colorize = True , format = "<green>{time}</green> <level>{message}</level>" )
جميع الأحواض المضافة إلى logger
هي آمن مؤشر ترابط افتراضيًا. فهي ليست آمنة متعددة المعالجة ، ولكن يمكنك enqueue
الرسائل لضمان سلامة السجلات. يمكن أيضًا استخدام هذه الوسيطة نفسها إذا كنت ترغب في تسجيل الدخول غير المتزامن.
logger . add ( "somefile.log" , enqueue = True )
يتم دعم وظائف Coroutine المستخدمة كحفقات أيضًا ويجب انتظارها بـ complete()
.
من المهم تتبع استثناءات التسجيل التي تحدث في التعليمات البرمجية الخاصة بك ، لكنها غير مجدية تمامًا إذا كنت لا تعرف سبب فشلها. يساعدك Loguru في تحديد المشكلات من خلال السماح بعرض تتبع المكدس بالكامل ، بما في ذلك قيم المتغيرات (شكرًا better_exceptions
على هذا!).
الرمز:
# Caution, "diagnose=True" is the default and may leak sensitive data in prod
logger . add ( "out.log" , backtrace = True , diagnose = True )
def func ( a , b ):
return a / b
def nested ( c ):
try :
func ( 5 , c )
except ZeroDivisionError :
logger . exception ( "What?!" )
nested ( 0 )
سوف يؤدي إلى:
2018-07-17 01: 38: 43.975 | خطأ | __main __: متداخل: 10 - ماذا؟!
Traceback (أحدث مكالمة أخيرة):
ملف "test.py" ، السطر 12 ، في <module>
متداخل (0)
└ <وظيفة متداخلة في 0x7F5C755322F0>
> ملف "test.py" ، السطر 8 ، في متداخل
FUNC (5 ، ج)
│ └ 0
└ <وظيفة FUNC في 0x7F5C79FC2E18>
ملف "Test.py" ، السطر 4 ، في Func
إرجاع A / B
│ └ 0
└ 5
ZerodivisionError: قسم من الصفر
لاحظ أن هذه الميزة لن تعمل على Python REPL الافتراضي بسبب بيانات الإطار غير المتوفرة.
انظر أيضًا: اعتبارات الأمان عند استخدام Loguru.
هل تريد أن يتم تسلسل السجلات الخاصة بك لتسهيل التحليل أو تمريرها؟ باستخدام الوسيطة serialize
، سيتم تحويل كل رسالة سجل إلى سلسلة JSON قبل إرسالها إلى الحوض المكون.
logger . add ( custom_sink_function , serialize = True )
باستخدام bind()
يمكنك وضع رسائل المسجل الخاصة بك عن طريق تعديل سمة السجل الإضافي.
logger . add ( "file.log" , format = "{extra[ip]} {extra[user]} {message}" )
context_logger = logger . bind ( ip = "192.168.0.1" , user = "someone" )
context_logger . info ( "Contextualize your logger easily" )
context_logger . bind ( user = "someone_else" ). info ( "Inline binding of extra attribute" )
context_logger . info ( "Use kwargs to add context during formatting: {user}" , user = "anybody" )
من الممكن تعديل حالة سياق محلي مؤقتًا مع contextualize()
:
with logger . contextualize ( task = task_id ):
do_something ()
logger . info ( "End of task" )
يمكنك أيضًا الحصول على مزيد من التحكم الدقيق في سجلاتك عن طريق الجمع بين bind()
filter
:
logger . add ( "special.log" , filter = lambda record : "special" in record [ "extra" ])
logger . debug ( "This message is not logged to the file" )
logger . bind ( special = True ). info ( "This message, though, is logged to the file!" )
أخيرًا ، تتيح طريقة patch()
إرفاق القيم الديناميكية بتهمة السجل لكل رسالة جديدة:
logger . add ( sys . stderr , format = "{extra[utc]} {message}" )
logger = logger . patch ( lambda record : record [ "extra" ]. update ( utc = datetime . utcnow ()))
في وقت ما ، ترغب في تسجيل المعلومات المطوورة دون عقوبة الأداء في الإنتاج ، يمكنك استخدام طريقة opt()
لتحقيق ذلك.
logger . opt ( lazy = True ). debug ( "If sink level <= DEBUG: {x}" , x = lambda : expensive_function ( 2 ** 64 ))
# By the way, "opt()" serves many usages
logger . opt ( exception = True ). info ( "Error stacktrace added to the log message (tuple accepted too)" )
logger . opt ( colors = True ). info ( "Per message <blue>colors</blue>" )
logger . opt ( record = True ). info ( "Display values from the record (eg. {record[thread]})" )
logger . opt ( raw = True ). info ( "Bypass sink formatting n " )
logger . opt ( depth = 1 ). info ( "Use parent stack context (useful within wrapped functions)" )
logger . opt ( capture = False ). info ( "Keyword arguments not added to {dest} dict" , dest = "extra" )
يأتي Loguru مع جميع مستويات التسجيل القياسية التي تتم إضافة trace()
و success()
. هل تحتاج المزيد؟ ثم ، فقط قم بإنشائها باستخدام وظيفة level()
.
new_level = logger . level ( "SNAKY" , no = 38 , color = "<yellow>" , icon = "?" )
logger . log ( "SNAKY" , "Here we go!" )
يتم الانتفاضة في التسجيل القياسي مع وسيط مثل datefmt
أو msecs
، %(asctime)s
و %(created)s
، أخصائيات البيانات الساذجة دون معلومات المنطقة الزمنية ، وليس التنسيق البديهي ، إلخ.
logger . add ( "file.log" , format = "{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}" )
من السهل استخدام المسجل في البرامج النصية الخاصة بك ، ويمكنك configure()
في البداية. لاستخدام Loguru من داخل المكتبة ، تذكر عدم الاتصال أبدًا add()
ولكن استخدم disable()
بدلاً من ذلك ، بحيث تصبح وظائف التسجيل بدون OP. إذا كان المطور يرغب في رؤية سجلات مكتبتك ، فيمكنه enable()
مرة أخرى.
# For scripts
config = {
"handlers" : [
{ "sink" : sys . stdout , "format" : "{time} - {message}" },
{ "sink" : "file.log" , "serialize" : True },
],
"extra" : { "user" : "someone" }
}
logger . configure ( ** config )
# For libraries, should be your library's `__name__`
logger . disable ( "my_library" )
logger . info ( "No matter added sinks, this message is not displayed" )
# In your application, enable the logger in the library
logger . enable ( "my_library" )
logger . info ( "This message however is propagated to the sinks" )
للراحة الإضافية ، يمكنك أيضًا استخدام مكتبة loguru-config
لإعداد logger
مباشرة من ملف التكوين.
هل ترغب في استخدام Handler
قطع الأشجار المدمج كمغسلة لوجورو؟
handler = logging . handlers . SysLogHandler ( address = ( 'localhost' , 514 ))
logger . add ( handler )
هل تحتاج إلى نشر رسائل loguru إلى قطع الأشجار القياسية؟
class PropagateHandler ( logging . Handler ):
def emit ( self , record : logging . LogRecord ) -> None :
logging . getLogger ( record . name ). handle ( record )
logger . add ( PropagateHandler (), format = "{message}" )
هل تريد اعتراض رسائل التسجيل القياسية نحو أحواض Loguru؟
class InterceptHandler ( logging . Handler ):
def emit ( self , record : logging . LogRecord ) -> None :
# Get corresponding Loguru level if it exists.
level : str | int
try :
level = logger . level ( record . levelname ). name
except ValueError :
level = record . levelno
# Find caller from where originated the logged message.
frame , depth = inspect . currentframe (), 0
while frame and ( depth == 0 or frame . f_code . co_filename == logging . __file__ ):
frame = frame . f_back
depth += 1
logger . opt ( depth = depth , exception = record . exc_info ). log ( level , record . getMessage ())
logging . basicConfig ( handlers = [ InterceptHandler ()], level = 0 , force = True )
لا تحب تنسيق المسجل الافتراضي؟ تفضل لون DEBUG
آخر؟ لا مشكلة:
# Linux / OSX
export LOGURU_FORMAT = "{time} | <lvl>{message}</lvl>"
# Windows
setx LOGURU_DEBUG_COLOR "<green>"
غالبًا ما يكون من المفيد استخراج معلومات محددة من سجلات تم إنشاؤها ، ولهذا السبب يوفر Loguru طريقة parse()
التي تساعد على التعامل مع السجلات و regexes.
pattern = r"(?P<time>.*) - (?P<level>[0-9]+) - (?P<message>.*)" # Regex with named groups
caster_dict = dict ( time = dateutil . parser . parse , level = int ) # Transform matching groups
for groups in logger . parse ( "file.log" , pattern , cast = caster_dict ):
print ( "Parsed:" , groups )
# {"level": 30, "message": "Log example", "time": datetime(2018, 12, 09, 11, 23, 55)}
يمكن دمج Loguru بسهولة مع مكتبة notifiers
الرائعة (يجب تثبيتها بشكل منفصل) لتلقي بريد إلكتروني عندما يفشل البرنامج بشكل غير متوقع أو لإرسال العديد من الإخطارات الأخرى.
import notifiers
params = {
"username" : "[email protected]" ,
"password" : "abc123" ,
"to" : "[email protected]"
}
# Send a single notification
notifier = notifiers . get_notifier ( "gmail" )
notifier . notify ( message = "The application is running!" , ** params )
# Be alerted on each error message
from notifiers . logging import NotificationHandler
handler = NotificationHandler ( "gmail" , defaults = params )
logger . add ( handler , level = "ERROR" )
على الرغم من أن التأثير في تسجيل الأداء لا يكاد يذكر في معظم الحالات ، فإن المسجل الصفر التكلفة سيسمح لاستخدامه في أي مكان دون القلق كثيرًا. في الإصدار القادم ، سيتم تنفيذ وظائف Loguru الحرجة في C لتحقيق أقصى سرعة.