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" )
ฟังก์ชั่นนี้ควรใช้ในการลงทะเบียน sinks ซึ่งรับผิดชอบในการจัดการข้อความบันทึกบริบทด้วยบันทึก dict อ่างล้างจานสามารถมีหลายรูปแบบ: ฟังก์ชั่นง่าย ๆ , เส้นทางสตริง, วัตถุคล้ายไฟล์, ฟังก์ชั่น coroutine หรือตัวจัดการในตัว
โปรดทราบว่าคุณสามารถ remove()
ตัวจัดการที่เพิ่มขึ้นก่อนหน้านี้โดยใช้ตัวระบุที่ส่งคืนในขณะที่เพิ่ม สิ่งนี้มีประโยชน์อย่างยิ่งหากคุณต้องการแทนที่ Handler 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()
Decorator / Context Manager ซึ่งทำให้มั่นใจได้ว่าข้อผิดพลาดใด ๆ จะถูกเผยแพร่ไปยัง 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>" )
sinks ทั้งหมดที่เพิ่มลงใน logger
จะปลอดภัยเธรดโดยค่าเริ่มต้น พวกเขาไม่ได้ปลอดภัยหลายกระบวนการ แต่คุณสามารถ enqueue
ข้อความไปใช้เพื่อให้แน่ใจว่ามีความสมบูรณ์ของบันทึก อาร์กิวเมนต์เดียวกันนี้สามารถใช้ได้หากคุณต้องการการบันทึกแบบ async
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, ใน <Domule>
ซ้อนกัน (0)
└ <ฟังก์ชั่นซ้อนกันที่ 0x7f5c7553222f0>
> ไฟล์ "test.py", บรรทัดที่ 8, ซ้อนกัน
func (5, c)
│ 0
└ <ฟังก์ชั่น func ที่ 0x7f5c79fc2e18>
ไฟล์ "test.py", บรรทัด 4, ใน func
คืน A / B
│ 0
└ 5
ZerodivisionError: Division By Zero
โปรดทราบว่าคุณลักษณะนี้จะไม่ทำงานใน Python เริ่มต้นเนื่องจากข้อมูลเฟรมที่ไม่สามารถใช้งานได้
ดูเพิ่มเติมที่: ข้อควรพิจารณาด้านความปลอดภัยเมื่อใช้ Loguru
ต้องการให้บันทึกของคุณเป็นอนุกรมเพื่อการแยกวิเคราะห์ที่ง่ายขึ้นหรือผ่านไปรอบ ๆ ? การใช้อาร์กิวเมนต์ serialize
ข้อความบันทึกแต่ละรายการจะถูกแปลงเป็นสตริง JSON ก่อนที่จะถูกส่งไปยังอ่างล้างจานที่กำหนดค่า
logger . add ( custom_sink_function , serialize = True )
การใช้ bind()
คุณสามารถทำให้ข้อความ Logger ของคุณมีบริบทโดยการแก้ไขแอตทริบิวต์บันทึกพิเศษ
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 ()))
บางครั้งคุณต้องการบันทึกข้อมูล verbose โดยไม่มีการลงโทษประสิทธิภาพในการผลิตคุณสามารถใช้วิธี 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
, NAIVE DATETIMES โดยไม่มีข้อมูลเขตเวลาไม่ใช่การจัดรูปแบบที่ใช้งานง่าย ฯลฯ LOGURU แก้ไข:
logger . add ( "file.log" , format = "{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}" )
การใช้เครื่องบันทึกในสคริปต์ของคุณนั้นง่ายและคุณสามารถ configure()
มันได้ตั้งแต่เริ่มต้น หากต้องการใช้ loguru จากภายในไลบรารีอย่าลืมโทร add()
แต่ใช้ disable()
แทนดังนั้นฟังก์ชั่นการเข้าสู่ระบบจึงไม่เป็นไปได้ หากนักพัฒนาต้องการเห็นบันทึกของห้องสมุดของคุณพวกเขาสามารถ 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
การบันทึกในตัวเป็นอ่างล้างมือ loguru หรือไม่?
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}" )
ต้องการสกัดกั้นข้อความการบันทึกมาตรฐานไปยัง Sinks 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 ด้วยความเร็วสูงสุด